Partager via


Comment : obtenir la progression à partir du programme d'installation du .NET Framework 4

Le .NET Framework version 4 est un runtime redistribuable. Vous pouvez inclure (chaîner) le processus d'installation .NET Framework 4 comme un composant prérequis dans l'installation de votre application. Si vous voulez offrir une installation personnalisée ou unifiée, vous pouvez lancer et suivre silencieusement l'installation de .NET Framework 4 en affichant votre propre vue de la progression de l'installation. Pour permettre le suivi silencieux, le processus d'installation .NET Framework 4 (l'élément chaîné, qui peut être observé) peut écrire le message de progression dans un segment E/S (MMIO) mappé en mémoire que votre processus d'installation (observateur ou programme de chaînage) peut lire. Vous pouvez annuler le processus d'installation .NET Framework 4 en écrivant un message Abort dans le segment MMIO.

  1. Appel. Pour appeler le processus d'installation .NET Framework 4 et recevoir les informations de progression de la section MMIO, votre programme d'installation doit effectuer les opérations suivantes :

    1. Appelez le programme redistribuable de .NET Framework 4 ; par exemple :

      dotnetfx40x86.exe /q /pipe <section name>
      

      où nom de la section correspond à un nom que vous souhaitez utiliser pour identifier votre application. L'élément chaîné lit et écrit de façon asynchrone dans la section MMIO, donc il peut être utile d'utiliser des événements et des messages pendant ce temps. Dans l'exemple, l'élément chaîné est créé par un constructeur qui alloue la section MMIO (YourSection) et définit un événement (YourEvent). Remplacez ces noms par des noms uniques à votre programme d'installation.

    2. Lisez à partir de la section MMIO. Dans le .NET Framework 4, les opérations d'installation et de téléchargement sont simultanées : un composant du .NET Framework 4 peut s'installer pendant le téléchargement d'un autre. Par conséquent, la progression est renvoyée à (autrement dit, écrite) dans la section MMIO comme un nombre qui augmente de 1 à 255. Lorsque 255 est écrit et que l'élément chaîné sort, l'installation est terminée.

  2. Codes de sortie. Les codes de sortie suivants de la commande pour appeler le programme redistribuable de .NET Framework 4 (consultez l'exemple précédent) indiquent si l'installation s'est terminée avec succès ou a échoué :

    • 0 - L'installation s'est terminée avec succès.

    • 3010 – L'installation s'est terminée avec succès ; un redémarrage est nécessaire.

    • 1642 – L'installation a été annulée.

    • Tous les autres codes - Le programme d'installation a rencontré des erreurs ; examinez les fichiers journaux créés dans%temp% pour plus d'informations.

  3. Annulation de l'installation. Vous pouvez annuler l'installation à tout moment à l'aide de la méthode Abort pour définir les indicateurs m_downloadAbort et m_ installAbort dans la section MMIO. Cela arrêtera le processus d'installation de .NET Framework 4.

Exemple de programme de chaînage

L'exemple suivant lance et suit silencieusement le processus d'installation de .NET Framework 4 tout en affichant la progression.

Remarque AttentionAttention

Vous devez exécuter l'exemple en tant qu'administrateur.

Vous pouvez télécharger la solution Visual Studio complète pour le Programme d'installation de Chaîne pour .NET Framework 4 à partir de Microsoft Code Gallery.

Les sections suivantes décrivent les fichiers importants dans cet exemple.

MmIoChainer.h

  • Le fichier MmIoChainer.h contient la définition de structure de données et la classe de base desquelles la classe du programme de chaînage doit être dérivée. La structure de données MMIO se compose du code suivant.

    // MMIO data structure for inter-process communication.
        struct MmioDataStructure
        {
            bool m_downloadFinished;        // Is download done yet?
            bool m_installFinished;         // Is installer operation done yet?
            bool m_downloadAbort;           // Set to cause downloader to abort.
            bool m_installAbort;            // Set to cause installer operation to abort.
            HRESULT m_hrDownloadFinished;   // HRESULT for download.
            HRESULT m_hrInstallFinished;    // HRESULT for installer operation.
            HRESULT m_hrInternalError;      // Internal error from MSI if applicable.
            WCHAR m_szCurrentItemStep[MAX_PATH];   // This identifies the windows installer step being executed if an error occurs while processing an MSI, for example, "Rollback".
            unsigned char m_downloadProgressSoFar; // Download progress 0 - 255 (0 to 100% done). 
            unsigned char m_installProgressSoFar;  // Install progress 0 - 255 (0 to 100% done).
            WCHAR m_szEventName[MAX_PATH];         // Event that chainer creates and chainee opens to sync communications.
        };
    
  • La structure de données est suivie de la structure de classe pour l'implémentation d'un programme de chaînage. Vous dérivez votre classe de serveur de la classe MmioChainer pour chaîner le redistribuable .NET Framework 4. La classe MmioChainerBase est utilisée par le programme de chaînage et l'élément chaîné. Dans le code suivant, les méthodes et membres ont été modifiés pour que l'exemple reste concis.

    // MmioChainerBase class manages the communication and synchronization data 
        // structures. It also implements common get accessors (for chainer) and set accessors(for chainee).
        class MmioChainerBase
        {
    ...
    
            // This is called by the chainer to force the chained setup to be canceled.
            void Abort()
            {
                //Don't do anything if it is invalid.
                if (NULL == m_pData)
                {
                    return;
                }
    ...
                // Chainer told us to cancel.
                m_pData->m_downloadAbort= true;
                m_pData->m_installAbort = true;
            }
    // Called when chainer wants to know if chained setup has finished both download and installation.
            bool IsDone() const 
            { 
    ...
            }
    
            // Called by the chainer to get the overall progress, i.e., the combination of the download and installation.
            unsigned char GetProgress() const 
            { 
    ...
            }
    
    
            // Get download progress.
            unsigned char GetDownloadProgress() const
            {
    ...
            }
    
            // Get installation progress.
            unsigned char GetInstallProgress() const
            {
    ...
            }
    
            // Get the combined setup result, installation taking priority over download if both failed.
            HRESULT GetResult() const
            { 
    ...
            }
    
    ...
        };
    
  • Le programme de chaînage est implémenté comme suit.

    // This is the class that the consumer (chainer) should derive from.
        class MmioChainer : protected MmioChainerBase
        {
        public:
            // Constructor
            MmioChainer (LPCWSTR sectionName, LPCWSTR eventName)
                : MmioChainerBase(CreateSection(sectionName), CreateEvent(eventName))
            {
                Init(eventName);
            }
    
            // Destructor
            virtual ~MmioChainer ()
            {
                ::CloseHandle(GetEventHandle());
                ::CloseHandle(GetMmioHandle());
            }
    
        public: // The public methods:  Abort and Run
            using MmioChainerBase::Abort;
            using MmioChainerBase::GetInstallResult;
            using MmioChainerBase::GetInstallProgress;
            using MmioChainerBase::GetDownloadResult;
            using MmioChainerBase::GetDownloadProgress;
            using MmioChainerBase::GetCurrentItemStep;
    
            HRESULT GetInternalErrorCode()
            {
                return GetInternalResult();
            }
    
            // Called by the chainer to start the chained setup. This blocks until setup is complete.
            void Run(HANDLE process, IProgressObserver& observer)
            {
                HANDLE handles[2] = { process, GetEventHandle() };
    
                while(!IsDone())
                {
                    DWORD ret = ::WaitForMultipleObjects(2, handles, FALSE, 100); // INFINITE ??
                    switch(ret)
                    {
                    case WAIT_OBJECT_0:
                        { // Process handle closed.  Maybe it blew up, maybe it's just really fast.  Let's find out.
                            if (IsDone() == false) // Not a good sign
                            {
                                HRESULT hr = GetResult();
                                if (hr == E_PENDING) // Untouched
                                    observer.Finished(E_FAIL);
                                else
                                    observer.Finished(hr);
    
                                return;
                            }
                            break;
                        }
                    case WAIT_OBJECT_0 + 1:
                        observer.OnProgress(GetProgress());
                        break;
                    default:
                        break;
                    }
                }
                observer.Finished(GetResult());
            }
    
        private:
            static HANDLE CreateSection(LPCWSTR sectionName)
            {
                return ::CreateFileMapping (INVALID_HANDLE_VALUE,
                    NULL, // Security attributes
                    PAGE_READWRITE,
                    0, // high-order DWORD of maximum size
                    sizeof(MmioDataStructure), // Low-order DWORD of maximum size.
                    sectionName);
            }
            static HANDLE CreateEvent(LPCWSTR eventName)
            {
                return ::CreateEvent(NULL, FALSE, FALSE, eventName);
            }
        };
    
  • L'élément chaîné dérive de la même classe de base.

    // This class is used by the chainee.
        class MmioChainee : protected MmioChainerBase
        {
        public:
            MmioChainee(LPCWSTR sectionName)
                : MmioChainerBase(OpenSection(sectionName), OpenEvent(GetEventName(sectionName)))
            {
            }
    
            virtual ~MmioChainee()
            {
            }
    
        private:
            static HANDLE OpenSection(LPCWSTR sectionName)
            {
                return ::OpenFileMapping(FILE_MAP_WRITE, // Read/write access.
                    FALSE,          // Do not inherit the name.
                    sectionName);
            }
    
            static HANDLE OpenEvent(LPCWSTR eventName)
            {        
                return ::OpenEvent (EVENT_MODIFY_STATE | SYNCHRONIZE,
                    FALSE,
                    eventName);
            }
    
            static CString GetEventName(LPCWSTR sectionName)
            {
                CString cs = L"";
    
                HANDLE handle = OpenSection(sectionName);
                if (NULL == handle)
                {
                    DWORD dw;
                    dw = GetLastError();
                    printf("OpenFileMapping fails with last error: %08x",dw);
                }
                else
                {
                    const MmioDataStructure* pData = MapView(handle);
                    if (pData)
                    {
                        cs = pData->m_szEventName;
                        ::UnmapViewOfFile(pData);
                    }
                    ::CloseHandle(handle);
                }
    
                return cs;
            }
        };
    

ChainingdotNet4.cpp

  • Vous pouvez dériver de la classe MmioChainer et substituer les méthodes appropriées pour afficher les informations de progression. Notez que MmioChainer fournit déjà une méthode Run() bloquante que la classe dérivée appellera. La classe Server dans le code suivant lance le programme d'installation spécifié, surveille sa progression et retourne un code de sortie.

    class Server : public ChainerSample::MmioChainer, public ChainerSample::IProgressObserver
    {
    public:
        // Mmiochainer will create section with given name. Create this section and the event name.
        // Event is also created by the Mmiochainer, and name is saved in the mapped data structure.
        Server():ChainerSample::MmioChainer(L"TheSectionName", L"TheEventName")
     // Customize for your event names.
        {}
    
        bool Launch(const CString& args)
        {
            CString cmdline = L"Setup.exe -pipe TheSectionName " + args; // Customize with name and location of setup .exe file 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.
            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);
    
                // To get the return code of the chainee, check exit code
                // of the chainee process after it exits.
                DWORD exitCode = 0;
    
                // Get the true return code.
                ::GetExitCodeProcess(pi.hProcess, &exitCode);
                printf("Exit code: %08X\n  ", exitCode);
    
                // Get internal result.
                // If the failure is in an MSI/MSP payload, the internal result refers to the error messages listed at
                // https://msdn.microsoft.com/en-us/library/aa372835(VS.85).aspx
                HRESULT hrInternalResult = GetInternalResult(); 
                printf("Internal result: %08X\n",hrInternalResult);
    
    
    
    
                ::CloseHandle(pi.hThread);
                ::CloseHandle(pi.hProcess);
            }
            else
            {
                printf("CreateProcess failed");
                ReportLastError();
            }
    
            return (bLaunchedSetup != 0);
        }
    
    private: // IProgressObserver
        virtual void OnProgress(unsigned char ubProgressSoFar)
        {
            printf("Progress: %i\n  ", ubProgressSoFar);
    
            // Testing: BEGIN - To test Abort behavior, uncomment the following code:
            //if (ubProgressSoFar > 127)
            //{
            //    printf("\rDeliberately Aborting with progress at %i  ", ubProgressSoFar);
            //    Abort();
            //}
            // Testing END
        }
    
        virtual void Finished(HRESULT hr)
        {
            // This HRESULT is communicated over MMIO and may be different from process
            // exit code of the chainee Setup.exe.
            printf("\r\nFinished HRESULT: 0x%08X\r\n", hr);
        }
    ...
    };
    

    Les données de progression seront un char non signé compris entre 0 (0 %) et 255 (100 %). La sortie de la méthode Finished est un HRESULT.

    Remarque importanteImportant

    Le redistribuable .NET Framework 4 écrit généralement de nombreux messages de progression et un message unique qui indique l'achèvement (sur le côté programme de chaînage).Il lit également de façon asynchrone, en recherchant des enregistrements Abort.S'il reçoit un enregistrement Abort, il annule l'installation et (finalement) écrit un enregistrement fini avec E_ABORT comme ses données.

    Un serveur classique crée un nom de fichier MMIO aléatoire, crée le fichier (comme indiqué dans l'exemple de code précédent, dans Server::CreateSection) et lance le redistribuable en utilisant CreateProcess, en passant le nom de canal avec le commutateur « -pipe someFileSectionName ». Le OnProgress du serveur et les méthodes Finished contiennent le code spécifique au serveur.

IprogressObserver.h

  • L'observateur de progression est averti de la progression (0-255) et de la fin de l'installation.

    #ifndef IPROGRESSOBSERVER_H
    #define IPROGRESSOBSERVER_H
    
    #include <oaidl.h>
    
    namespace ChainerSample
    {
        class IProgressObserver
        {
        public:
            virtual void OnProgress(unsigned char) = 0; // 0 - 255:  255 == 100%
            virtual void Finished(HRESULT) = 0;         // Called when operation is complete.
        };
    }
    #endif
    
  • Le HRESULT est passé à la méthode Finished. Le code qui suit montre le point d'entrée principal au programme.

    // Main entry point for program.
    int __cdecl main(int argc, _In_count_(argc) char **_argv)
    {
        CString args;
        if (argc > 1)
        {
            args = CString(_argv[1]);
        }
    
        return Server().Launch(args);
    }
    
  • Modifiez le chemin d'accès de l'exécutable (Setup.exe dans l'exemple) pour pointer sur son emplacement correct ou modifiez le code pour le comprendre. Vous devez exécuter le code en tant qu'administrateur.

Voir aussi

Concepts

Déploiement d'applications et du .NET Framework

Autres ressources

Guide de déploiement du .NET Framework pour les développeurs

Historique des modifications

Date

Historique

Motif

Juillet 2010

Ajout d'une rubrique.

Améliorations apportées aux informations.