Como: Obter progresso do instalador do .NET Framework 4.5

O .NET Framework 4.5 é um tempo de execução redistribuível. Se você desenvolver aplicativos para esta versão do .NET Framework, poderá incluir a instalação (em cadeia) do .NET Framework 4.5 como uma parte de pré-requisito da configuração do seu aplicativo. Para apresentar uma experiência de instalação personalizada ou unificada, convém iniciar silenciosamente a instalação do .NET Framework 4.5 e acompanhar seu progresso enquanto mostra o progresso da instalação do seu aplicativo. Para habilitar o rastreamento silencioso, a instalação do .NET Framework 4.5 (que pode ser observada) define um protocolo usando um segmento de E/S (MMIO) mapeado na memória para se comunicar com sua configuração (o observador ou o chainer). Esse protocolo define uma maneira para um encadeador obter informações de progresso, obter resultados detalhados, responder a mensagens e cancelar a instalação do .NET Framework 4.5.

  • Invocação. Para chamar a instalação do .NET Framework 4.5 e receber informações de progresso da seção MMIO, o programa de instalação deve fazer o seguinte:

    1. Chame o programa redistribuível do .NET Framework 4.5:

      dotNetFx45_Full_x86_x64.exe /q /norestart /pipe section-name

      Onde nome da seção é qualquer nome que você queira usar para identificar seu aplicativo. A instalação do .NET Framework lê e grava na seção MMIO de forma assíncrona, portanto, você pode achar conveniente usar eventos e mensagens durante esse período. No exemplo, o processo de instalação do .NET Framework é criado por um construtor que aloca a seção MMIO (TheSectionName) e define um evento (TheEventName):

      Server():ChainerSample::MmioChainer(L"TheSectionName", L"TheEventName")
      

      Substitua esses nomes por nomes exclusivos do seu programa de configuração.

    2. Leia a seção MMIO. No .NET Framework 4.5, as operações de download e instalação são simultâneas: uma parte do .NET Framework pode estar sendo instalada enquanto outra parte está sendo baixada. Como resultado, o progresso é enviado de volta (ou seja, escrito) para a seção MMIO como dois números (m_downloadSoFar e m_installSoFar) que aumentam de 0 para 255. Quando 255 é escrito e o .NET Framework é encerrado, a instalação é concluída.

  • Códigos de saída. Os seguintes códigos de saída do comando para chamar o programa redistribuível do .NET Framework 4.5 indicam se a instalação foi bem-sucedida ou falhou:

    • 0 - Instalação concluída com êxito.

    • 3010 – Configuração concluída com êxito; É necessário reiniciar o sistema.

    • 1602 – A instalação foi cancelada.

    • Todos os outros códigos - O programa de configuração encontrou erros; Examine os arquivos de log criados em %temp% para obter detalhes.

  • Cancelando a configuração. Você pode cancelar a instalação a qualquer momento usando o Abort método para definir os m_downloadAbort sinalizadores e m_ installAbort na seção MMIO.

Amostra de encadeador

O exemplo Chainer inicia silenciosamente e rastreia a instalação do .NET Framework 4.5 enquanto mostra o progresso. Este exemplo é semelhante ao exemplo Chainer fornecido para o .NET Framework 4. No entanto, além disso, ele pode evitar reinicializações do sistema processando a caixa de mensagem para fechar aplicativos do .NET Framework 4. Para obter informações sobre essa caixa de mensagem, consulte Reduzindo reinicializações do sistema durante instalações do .NET Framework 4.5. Você pode usar este exemplo com o instalador do .NET Framework 4; Nesse cenário, a mensagem simplesmente não é enviada.

Aviso

Você deve executar o exemplo como administrador.

As seções a seguir descrevem os arquivos significativos neste exemplo: MMIOChainer.h, ChainingdotNet4.cpp e IProgressObserver.h.

MMIOChainer.h

  • O arquivo MMIOChainer.h contém a definição da estrutura de dados e a classe base da qual a classe chainer deve ser derivada. O .NET Framework 4.5 estende a estrutura de dados MMIO para manipular dados que o instalador do .NET Framework 4.5 precisa. As alterações na estrutura do MMIO são compatíveis com versões anteriores, portanto, um encadeador do .NET Framework 4 pode trabalhar com a instalação do .NET Framework 4.5 sem exigir recompilação. No entanto, esse cenário não oferece suporte ao recurso para reduzir as reinicializações do sistema.

    Um campo de versão fornece um meio de identificar revisões na estrutura e no formato da mensagem. A instalação do .NET Framework determina a versão da interface do chainer chamando a VirtualQuery função para determinar o tamanho do mapeamento de arquivo. Se o tamanho for grande o suficiente para acomodar o campo de versão, a instalação do .NET Framework usará o valor especificado. Se o mapeamento de arquivo for muito pequeno para conter um campo de versão, que é o caso do .NET Framework 4, o processo de instalação assume a versão 0 (4). Se o encadeador não suportar a versão da mensagem que a instalação do .NET Framework deseja enviar, a instalação do .NET Framework assumirá uma resposta ignorada.

    A estrutura de dados do MMIO é definida da seguinte forma:

    // MMIO data structure for interprocess communication
        struct MmioDataStructure
        {
            bool m_downloadFinished;               // Is download complete?
            bool m_installFinished;                // Is installation complete?
            bool m_downloadAbort;                  // Set to cause downloader to abort.
            bool m_installAbort;                   // Set to cause installer to abort.
            HRESULT m_hrDownloadFinished;          // Resulting HRESULT for download.
            HRESULT m_hrInstallFinished;           // Resulting HRESULT for installation.
            HRESULT m_hrInternalError;
            WCHAR m_szCurrentItemStep[MAX_PATH];
            unsigned char m_downloadSoFar;         // Download progress 0-255 (0-100% done).
            unsigned char m_installSoFar;          // Installation progress 0-255 (0-100% done).
            WCHAR m_szEventName[MAX_PATH];         // Event that chainer creates and chainee opens to sync communications.
    
            BYTE m_version;                        // Version of the data structure, set by chainer:
                                                   // 0x0: .NET Framework 4
                                                   // 0x1: .NET Framework 4.5
    
            DWORD m_messageCode;                   // Current message sent by the chainee; 0 if no message is active.
            DWORD m_messageResponse;               // Chainer's response to current message; 0 if not yet handled.
            DWORD m_messageDataLength;             // Length of the m_messageData field, in bytes.
            BYTE m_messageData[1];                 // Variable-length buffer; content depends on m_messageCode.
        };
    
  • A MmioDataStructure estrutura de dados não deve ser usada diretamente, use a MmioChainer classe para implementar seu chainer. Derive da MmioChainer classe para encadear o .NET Framework 4.5 redistribuível.

IProgressObserver.h

  • O arquivo IProgressObserver.h implementa um observador de progresso. Este observador é notificado sobre o progresso do download e da instalação (especificado como um não assinado char, 0-255, indicando 1%-100% concluído). O observador também é notificado quando o chainee envia uma mensagem, e o observador deve enviar uma resposta.

        class IProgressObserver
        {
        public:
            virtual void OnProgress(unsigned char) = 0; // 0 - 255:  255 == 100%
            virtual void Finished(HRESULT) = 0;         // Called when operation is complete
            virtual DWORD Send(DWORD dwMessage, LPVOID pData, DWORD dwDataLength) = 0; // Called when a message is sent
        };
    

ChainingdotNet4.5.cpp

  • O arquivo ChainingdotNet4.5.cpp implementa a Server classe, que deriva da MmioChainer classe e substitui os métodos apropriados para exibir informações de progresso. O MmioChainer cria uma seção com o nome da seção especificada e inicializa o chainer com o nome do evento especificado. O nome do evento é salvo na estrutura de dados mapeada. Você deve tornar os nomes de seção e de evento exclusivos. A Server classe no código a seguir inicia o programa de instalação especificado, monitora seu progresso e retorna um código de saída.

    class Server : public ChainerSample::MmioChainer, public ChainerSample::IProgressObserver
    {
    public:
        …………….
        Server():ChainerSample::MmioChainer(L"TheSectionName", L"TheEventName") //customize for your event names
        {}
    

    A instalação é iniciada no método Main.

    // Main entry point for program
    int __cdecl main(int argc, _In_count_(argc) char **_argv)
    {
        int result = 0;
        CString args;
        if (argc > 1)
        {
            args = CString(_argv[1]);
        }
    
        if (IsNetFx4Present(NETFX45_RC_REVISION))
        {
            printf(".NET Framework 4.5 is already installed");
        }
        else
        {
            result = Server().Launch(args);
        }
    
        return result;
    }
    
  • Antes de iniciar a instalação, o encadeador verifica se o .NET Framework 4.5 já está instalado chamando IsNetFx4Present:

    ///  Checks for presence of the .NET Framework 4.
    ///    A value of 0 for dwMinimumRelease indicates a check for the .NET Framework 4 full
    ///    Any other value indicates a check for a specific compatible release of the .NET Framework 4.
    #define NETFX40_FULL_REVISION 0
    // TODO: Replace with released revision number
    #define NETFX45_RC_REVISION MAKELONG(50309, 5)   // .NET Framework 4.5
    bool IsNetFx4Present(DWORD dwMinimumRelease)
    {
        DWORD dwError = ERROR_SUCCESS;
        HKEY hKey = NULL;
        DWORD dwData = 0;
        DWORD dwType = 0;
        DWORD dwSize = sizeof(dwData);
    
        dwError = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full", 0, KEY_READ, &hKey);
        if (ERROR_SUCCESS == dwError)
        {
            dwError = ::RegQueryValueExW(hKey, L"Release", 0, &dwType, (LPBYTE)&dwData, &dwSize);
    
            if ((ERROR_SUCCESS == dwError) && (REG_DWORD != dwType))
            {
                dwError = ERROR_INVALID_DATA;
            }
            else if (ERROR_FILE_NOT_FOUND == dwError)
            {
                // Release value was not found, let's check for 4.0.
                dwError = ::RegQueryValueExW(hKey, L"Install", 0, &dwType, (LPBYTE)&dwData, &dwSize);
    
                // Install = (REG_DWORD)1;
                if ((ERROR_SUCCESS == dwError) && (REG_DWORD == dwType) && (dwData == 1))
                {
                    // treat 4.0 as Release = 0
                    dwData = 0;
                }
                else
                {
                    dwError = ERROR_INVALID_DATA;
                }
            }
        }
    
        if (hKey != NULL)
        {
            ::RegCloseKey(hKey);
        }
    
        return ((ERROR_SUCCESS == dwError) && (dwData >= dwMinimumRelease));
    }
    
  • Você pode alterar o caminho do executável (Setup.exe no exemplo) no Launch método para apontar para seu local correto ou personalizar o código para determinar o local. A MmioChainer classe base fornece um método de bloqueio Run() que a classe derivada chama.

    bool Launch(const CString& args)
    {
    CString cmdline = L"dotNetFx45_Full_x86_x64.exe -pipe TheSectionName " + args; // Customize with name and location of setup .exe that you want to run
    STARTUPINFO si = {0};
    si.cb = sizeof(si);
    PROCESS_INFORMATION pi = {0};
    
    // Launch the Setup.exe that installs the .NET Framework 4.5
    BOOL bLaunchedSetup = ::CreateProcess(NULL,
     cmdline.GetBuffer(),
     NULL, NULL, FALSE, 0, NULL, NULL,
     &si,
     &pi);
    
    // If successful
    if (bLaunchedSetup != 0)
    {
    IProgressObserver& observer = dynamic_cast<IProgressObserver&>(*this);
    Run(pi.hProcess, observer);
    
    ……………………..
    return (bLaunchedSetup != 0);
    }
    
  • O Send método interceta e processa as mensagens. Nesta versão do .NET Framework, a única mensagem suportada é a mensagem fechar aplicativo.

            // SendMessage
            //
            // Send a message and wait for the response.
            // dwMessage: Message to send
            // pData: The buffer to copy the data to
            // dwDataLength: Initially a pointer to the size of pBuffer. Upon successful call, the number of bytes copied to pBuffer.
            //--------------------------------------------------------------
        virtual DWORD Send(DWORD dwMessage, LPVOID pData, DWORD dwDataLength)
        {
            DWORD dwResult = 0;
            printf("received message: %d\n", dwMessage);
            // Handle message
            switch (dwMessage)
            {
            case MMIO_CLOSE_APPS:
                {
                    printf("    applications are holding files in use:\n");
                    IronMan::MmioCloseApplications* applications = reinterpret_cast<IronMan::MmioCloseApplications*>(pData);
                    for(DWORD i = 0; i < applications->m_dwApplicationsSize; i++)
                    {
                        printf("      %ls (%d)\n", applications->m_applications[i].m_szName, applications->m_applications[i].m_dwPid);
                    }
    
                    printf("    should applications be closed? (Y)es, (N)o, (R)efresh : ");
                    while (dwResult == 0)
                    {
                        switch (toupper(getwchar()))
                        {
                        case 'Y':
                            dwResult = IDYES;  // Close apps
                            break;
                        case 'N':
                            dwResult = IDNO;
                            break;
                        case 'R':
                            dwResult = IDRETRY;
                            break;
                        }
                    }
                    printf("\n");
                    break;
                }
            default:
                break;
            }
            printf("  response: %d\n  ", dwResult);
            return dwResult;
        }
    };
    
  • Os dados de progresso são não assinados char entre 0 (0%) e 255 (100%).

    private: // IProgressObserver
        virtual void OnProgress(unsigned char ubProgressSoFar)
        {…………
       }
    
  • O HRESULT é passado para o Finished método.

    virtual void Finished(HRESULT hr)
    {
    // This HRESULT is communicated over MMIO and may be different than process
    // Exit code of the Chainee Setup.exe itself
    printf("\r\nFinished HRESULT: 0x%08X\r\n", hr);
    }
    

    Importante

    O .NET Framework 4.5 redistribuível normalmente grava muitas mensagens de progresso e uma única mensagem que indica a conclusão (no lado do encadeador). Também lê de forma assíncrona, procurando Abort registros. Se ele receber um Abort registro, ele cancelará a instalação e gravará um registro concluído com E_ABORT como seus dados depois que a instalação tiver sido abortada e as operações de instalação tiverem sido revertidas.

Um servidor típico cria um nome de arquivo MMIO aleatório, cria o arquivo (como mostrado no exemplo de código anterior, em Server::CreateSection) e inicia o redistribuível usando o CreateProcess método e passando o nome do pipe com a -pipe someFileSectionName opção. O servidor deve implementar OnProgress, Sende Finished métodos com código específico da interface do usuário do aplicativo.

Consulte também