Como: Obter progresso do instalador .NET Framework 4.5

O .NET Framework 4,5 é um tempo de execução redistribuível. Se desenvolver aplicativos para esta versão do .NET Framework, pode incluir (cadeia) .NET Framework configuração 4.5 como parte pré-requisito da configuração da sua aplicação. Para apresentar uma experiência de configuração personalizada ou unificada, pode querer lançar silenciosamente .NET Framework configuração 4.5 e acompanhar o seu progresso enquanto mostra o progresso da configuração da sua aplicação. Para permitir o rastreio silencioso, .NET Framework configuração 4.5 (que pode ser observada) define um protocolo utilizando um segmento de I/O (MMIO) mapeado pela memória para comunicar com a sua configuração (o observador ou a corrente). Este protocolo define uma forma de um acorrentador obter informações de progresso, obter resultados detalhados, responder a mensagens e cancelar a configuração .NET Framework 4.5.

  • A invocação. Para ligar para .NET Framework configuração 4.5 e receber informações de progresso da secção MMIO, o seu programa de configuração deve fazer o seguinte:

    1. Ligue para o programa redistribuído .NET Framework 4.5:

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

      Onde o nome da secção é qualquer nome que pretende usar para identificar a sua aplicação. .NET Framework configuração lê e escreve para a secção MMIO assincroticamente, para que possa achar conveniente usar eventos e mensagens durante esse tempo. No exemplo, o processo de configuração .NET Framework é criado por um construtor que atribui a secção MMIO (TheSectionName) e define um evento (TheEventName):

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

      Por favor, substitua os nomes por nomes exclusivos do seu programa de configuração.

    2. Leia na secção MMIO. No .NET Framework 4.5, as operações de descarregamento e instalação são simultâneas: Uma parte do .NET Framework pode estar a instalar enquanto outra parte está a ser descarregada. Como resultado, os progressos são enviados de volta (isto é, escritos) para a secção MMIO como dois números (m_downloadSoFar e m_installSoFar) que aumentam de 0 para 255. Quando 255 é escrito e o .NET Framework sai, a instalação está completa.

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

    • 0 - Configuração concluída com sucesso.

    • 3010 - Configuração concluída com sucesso; é necessário reiniciar o sistema.

    • 1602 - A configuração foi cancelada.

    • Todos os outros códigos - Configuração encontrou erros; examinar os ficheiros de registo criados em %temperatura% para obter detalhes.

  • Cancelamento de configuração. Pode cancelar a configuração a qualquer momento utilizando o Abort método para definir as m_downloadAbort bandeiras e m_ installAbort bandeiras na secção MMIO.

Amostra de corrente

A amostra Chainer lança e rastreia silenciosamente .NET Framework configuração 4.5 enquanto mostra progresso. Esta amostra é semelhante à amostra chainer fornecida para o .NET Framework 4. No entanto, além disso, pode evitar o reinício do sistema através do processamento da caixa de mensagens para fechar .NET Framework 4 aplicações. Para obter informações sobre esta caixa de mensagens, consulte o Sistema redutor reinicia durante .NET Framework 4.5 Instalações. Pode utilizar esta amostra com o instalador .NET Framework 4; nesse cenário, a mensagem simplesmente não é enviada.

Aviso

Tem de correr o exemplo como administrador.

As seguintes secções descrevem os ficheiros significativos desta amostra: MMIOChainer.h, ChainingdotNet4.cpp e IProgressObserver.h.

MMIOChainer.h

  • O ficheiro MMIOChainer.h contém a definição da estrutura de dados e a classe base a partir da qual a classe de corrente deve ser derivada. O .NET Framework 4.5 alarga a estrutura de dados do MMIO para lidar com os dados de que o instalador .NET Framework 4.5 necessita. As alterações na estrutura MMIO são retro-compatíveis, pelo que um .NET Framework 4 chainer pode trabalhar com .NET Framework configuração 4.5 sem exigir a recompilação. No entanto, este cenário não suporta a funcionalidade de redução do sistema.

    Um campo de versão fornece um meio de identificar revisões para o formato estrutura e mensagem. A configuração .NET Framework determina a versão da interface do chainer, ligando para a VirtualQuery função para determinar o tamanho do mapeamento do ficheiro. Se o tamanho for suficientemente grande para acomodar o campo de versão, .NET Framework configuração utiliza o valor especificado. Se o mapeamento de ficheiros for demasiado pequeno para conter um campo de versão, que é o caso do .NET Framework 4, o processo de configuração pressupõe a versão 0 (4). Se o acorrentador não suportar a versão da mensagem que .NET Framework configuração quer enviar, .NET Framework configuração pressupõe uma resposta ignorante.

    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 utilizada diretamente; utilize a classe para implementar a MmioChainer sua corrente. Deriva da MmioChainer classe para acorrentar o .NET Framework 4,5 redistribuable.

IProgressObserver.h

  • O ficheiro IProgressObserver.h implementa um observador de progresso. Este observador é notificado do progresso do download e da instalação (especificado como um não assinado char, 0-255, indicando 1%-100% completo). O observador é também notificado quando o acorrentado envia uma mensagem, devendo o observador 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 ficheiro ChainingdotNet4.5.cpp implementa a Server classe, que deriva da classe e substitui os MmioChainer métodos adequados para exibir informações de progresso. O MmioChainer cria uma secção com o nome de secção especificado e rubrica o chainer com o nome do evento especificado. O nome do evento é guardado na estrutura de dados mapeada. Você deve tornar a secção e os nomes do evento únicos. A Server classe no código seguinte lança o programa de configuração especificado, monitoriza o seu progresso e devolve 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 Principal.

    // 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, a corrente verifica se o .NET Framework 4.5 já está instalado ligando: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));
    }
    
  • Pode alterar o caminho do executável (Setup.exe no exemplo) no Launch método para apontar para a sua localização correta, ou personalizar o código para determinar a localização. A MmioChainer classe base fornece um método de Run() bloqueio 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 de aplicação próxima.

            // 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 um não assinado 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 escreve muitas mensagens de progresso e uma única mensagem que indica a conclusão (no lado da corrente). Também lê assíncronosamente, à procura Abort de registos. Se receber um Abort registo, cancela a instalação e escreve um registo final com E_ABORT como os seus dados após a instalação ter sido abortada e as operações de configuração terem sido reviradas.

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

Ver também