Porady: pobieranie danych o postępie z Instalatora .NET Framework 4.5

Program .NET Framework 4.5 jest środowiskiem uruchomieniowym redystrybucyjnym. Jeśli tworzysz aplikacje dla tej wersji programu .NET Framework, możesz uwzględnić konfigurację programu .NET Framework 4.5 (łańcuch) jako część wymagań wstępnych konfiguracji aplikacji. Aby przedstawić dostosowane lub ujednolicone środowisko konfiguracji, możesz w trybie dyskretnym uruchomić konfigurację programu .NET Framework 4.5 i śledzić jego postęp podczas wyświetlania postępu instalacji aplikacji. Aby włączyć śledzenie dyskretne, konfiguracja programu .NET Framework 4.5 (którą można obserwować) definiuje protokół przy użyciu segmentu we/wy mapowanego w pamięci (MMIO) do komunikowania się z konfiguracją (obserwator lub łańcuch). Ten protokół definiuje sposób uzyskiwania informacji o postępie, uzyskiwania szczegółowych wyników, reagowania na komunikaty i anulowania konfiguracji programu .NET Framework 4.5.

  • Wywołanie. Aby wywołać instalatora programu .NET Framework 4.5 i uzyskać informacje o postępie z sekcji MMIO, program instalacyjny musi wykonać następujące czynności:

    1. Wywołaj program redystrybucyjny programu .NET Framework 4.5:

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

      Gdzie nazwa sekcji to dowolna nazwa, której chcesz użyć do identyfikacji aplikacji. Konfiguracja programu .NET Framework odczytuje i zapisuje w sekcji MMIO asynchronicznie, dzięki czemu w tym czasie może być wygodne korzystanie ze zdarzeń i komunikatów. W tym przykładzie proces instalacji programu .NET Framework jest tworzony przez konstruktor, który przydziela sekcję MMIO (TheSectionName) i definiuje zdarzenie (TheEventName):

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

      Zastąp te nazwy nazwami unikatowymi dla programu instalacyjnego.

    2. Przeczytaj sekcję MMIO. W programie .NET Framework 4.5 operacje pobierania i instalacji są równoczesne: jedna część programu .NET Framework może być instalowana podczas pobierania innej części. W rezultacie postęp jest wysyłany z powrotem (czyli zapisywany) do sekcji MMIO jako dwie liczby (m_downloadSoFar i m_installSoFar), które zwiększają się z 0 do 255. Po zapisaniu wartości 255 i zakończeniu działania programu .NET Framework instalacja zostanie ukończona.

  • Kody zakończenia. Następujące kody zakończenia polecenia w celu wywołania programu redystrybucyjnego programu .NET Framework 4.5 wskazują, czy instalacja zakończyła się powodzeniem, czy niepowodzeniem:

    • 0 — Instalacja została ukończona pomyślnie.

    • 3010 — Instalacja została ukończona pomyślnie; wymagane jest ponowne uruchomienie systemu.

    • 1602 — Instalacja została anulowana.

    • Wszystkie inne kody — Instalator napotkał błędy; sprawdź pliki dziennika utworzone w %temp%, aby uzyskać szczegółowe informacje.

  • Anulowanie konfiguracji. Konfigurację można anulować w dowolnym momencie przy użyciu Abort metody , aby ustawić m_downloadAbort flagi i m_ installAbort w sekcji MMIO.

Przykład chainer

Przykład Chainer w trybie dyskretnym uruchamia i śledzi konfigurację programu .NET Framework 4.5 podczas wyświetlania postępu. Ten przykład jest podobny do przykładu Chainer udostępnionego dla programu .NET Framework 4. Ponadto można uniknąć ponownych uruchomień systemu przez przetworzenie pola komunikatu w celu zamknięcia aplikacji programu .NET Framework 4. Aby uzyskać informacje o tym oknie komunikatu, zobacz Zmniejszanie ponownych uruchomień systemu podczas instalacji programu .NET Framework 4.5. Możesz użyć tego przykładu z instalatorem programu .NET Framework 4; w tym scenariuszu wiadomość nie jest po prostu wysyłana.

Ostrzeżenie

Musisz uruchomić przykład jako administrator.

W poniższych sekcjach opisano istotne pliki w tym przykładzie: MMIOChainer.h, ChainingdotNet4.cpp i IProgressObserver.h.

MMIOChainer.h

  • Plik MMIOChainer.h zawiera definicję struktury danych i klasę bazową, z której powinna pochodzić klasa łańcucha. Program .NET Framework 4.5 rozszerza strukturę danych MMIO w celu obsługi danych potrzebnych przez instalator programu .NET Framework 4.5. Zmiany struktury MMIO są zgodne z poprzednimi wersjami, więc łańcuch programu .NET Framework 4 może współpracować z konfiguracją programu .NET Framework 4.5 bez konieczności ponownego komplikowania. Jednak ten scenariusz nie obsługuje funkcji zmniejszania liczby ponownych uruchomień systemu.

    Pole wersji zawiera metodę identyfikowania poprawek struktury i formatu komunikatów. Instalator programu .NET Framework określa wersję interfejsu łańcucha, wywołując VirtualQuery funkcję w celu określenia rozmiaru mapowania plików. Jeśli rozmiar jest wystarczająco duży, aby pomieścić pole wersji, instalator programu .NET Framework używa określonej wartości. Jeśli mapowanie plików jest zbyt małe, aby zawierało pole wersji, w przypadku programu .NET Framework 4 proces instalacji zakłada wersję 0 (4). Jeśli moduł łańcuchowy nie obsługuje wersji komunikatu, którą instalator programu .NET Framework chce wysłać, instalator programu .NET Framework zakłada ignorowanie odpowiedzi.

    Struktura danych MMIO jest definiowana w następujący sposób:

    // 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.
        };
    
  • Struktura MmioDataStructure danych nie powinna być używana bezpośrednio; zamiast tego użyj MmioChainer klasy , aby zaimplementować łańcuch. MmioChainer Z klasy należy utworzyć łańcuch redystrybucyjny programu .NET Framework 4.5.

IProgressObserver.h

  • Plik IProgressObserver.h implementuje obserwatora postępu. Ten obserwator otrzymuje powiadomienie o postępie pobierania i instalacji (określony jako niepodpisany char, 0–255 wskazujący ukończenie 1%-100%. Obserwator jest również powiadamiany, gdy łańcuch wysyła wiadomość, a obserwator powinien wysłać odpowiedź.

        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

  • Plik ChainingdotNet4.5.cpp implementuje klasę Server , która pochodzi z MmioChainer klasy i zastępuje odpowiednie metody wyświetlania informacji o postępie. MmioChainer tworzy sekcję o określonej nazwie sekcji i inicjuje łańcuch z określoną nazwą zdarzenia. Nazwa zdarzenia jest zapisywana w mapowanej strukturze danych. Należy ustawić unikatowe nazwy sekcji i zdarzeń. Klasa Server w poniższym kodzie uruchamia określony program instalacyjny, monitoruje postęp i zwraca kod zakończenia.

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

    Instalacja została uruchomiona w metodzie 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;
    }
    
  • Przed uruchomieniem instalacji moduł łańcuchowy sprawdza, czy program .NET Framework 4.5 jest już zainstalowany, wywołując polecenie 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));
    }
    
  • Możesz zmienić ścieżkę pliku wykonywalnego (Setup.exe w przykładzie Launch ) w metodzie, aby wskazać poprawną lokalizację lub dostosować kod w celu określenia lokalizacji. Klasa MmioChainer bazowa udostępnia metodę blokującą Run() wywoływaną przez klasę pochodną.

    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);
    }
    
  • Metoda Send przechwytuje i przetwarza komunikaty. W tej wersji programu .NET Framework jedynym obsługiwanym komunikatem jest komunikat zamykanej aplikacji.

            // 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;
        }
    };
    
  • Dane postępu są niepodpisane char z zakresu od 0 (0%) do 255 (100%).

    private: // IProgressObserver
        virtual void OnProgress(unsigned char ubProgressSoFar)
        {…………
       }
    
  • HrESULT jest przekazywany do Finished metody .

    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);
    }
    

    Ważne

    Redystrybucyjny program .NET Framework 4.5 zwykle zapisuje wiele komunikatów postępu i pojedynczy komunikat wskazujący ukończenie (po stronie łańcucha). Odczytuje również asynchronicznie, wyszukując Abort rekordy. Jeśli otrzyma rekord, anuluje instalację Abort i zapisuje gotowy rekord z E_ABORT jako jego dane po przerwaniu instalacji i wycofaniu operacji instalacji.

Typowy serwer tworzy losową nazwę pliku MMIO, tworzy plik (jak pokazano w poprzednim przykładzie kodu w pliku ) Server::CreateSectioni uruchamia pakiet redystrybucyjny przy użyciu CreateProcess metody i przekazuje nazwę potoku z opcją -pipe someFileSectionName . Serwer powinien implementować OnProgressmetody , Sendi Finished z kodem specyficznym dla interfejsu użytkownika aplikacji.

Zobacz też