Condividi tramite


Procedura: ottenere lo stato di avanzamento dal programma d'installazione di .NET Framework 4

.NET Framework versione 4 è un runtime ridistribuibile. È possibile includere (concatenare) il processo di installazione di .NET Framework 4 come componente prerequisito nell'installazione dell'applicazione. Per offrire un'esperienza d'installazione personalizzata o unificata, è possibile avviare l'installazione di .NET Framework 4 in modo invisibile all'utente, tenendone traccia durante la visualizzazione dello stato di avanzamento dell'installazione. Per abilitare la traccia invisibile all'utente, il processo di installazione di .NET Framework 4 (elemento concatenato che può essere controllato) può scrivere un messaggio di avanzamento in un segmento IO mappato alla memoria (MMIO) che il processo di installazione (il controllo o il concatenatore) può leggere. È possibile annullare il processo di installazione di .NET Framework 4 scrivendo un messaggio Abort al segmento MMIO.

  1. Chiamata. Per chiamare il processo di installazione di .NET Framework 4 e ricevere informazioni sullo stato di avanzamento dalla sezione MMIO, il programma di installazione deve eseguire le operazioni seguenti:

    1. Chiamare il programma ridistribuibile .NET Framework 4; ad esempio:

      dotnetfx40x86.exe /q /pipe <section name>
      

      dove nome sezione è il nome che si desidera utilizzare per identificare l'applicazione. L'elemento concatenato legge e scrive in modo asincrono nella sezione MMIO, quindi potrebbe essere utile utilizzare eventi e messaggi in quell'intervallo di tempo. Nell'esempio, l'elemento concatenato viene creato da un costruttore che alloca la sezione MMIO (YourSection) e definisce un evento (YourEvent). Sostituire tali nomi con quelli univoci per il programma di installazione.

    2. Leggere dalla sezione MMIO. In .NET Framework 4, le operazioni di download e installazione sono simultanee: potrebbero essere contemporaneamente in corso l'installazione e il download di un componente di .NET Framework 4. Di conseguenza, lo stato di avanzamento viene restituito a (ovvero, scritto) alla sezione MMIO come un numero crescente da 1 a 255. Quando 255 viene scritto e l'elemento concatenato esce, l'installazione è completa.

  2. Codici di uscita. I seguenti codici di uscita dal comando per il richiamo del programma ridistribuibile .NET Framework 4 (vedere l'esempio precedente) indicano se l'installazione è riuscita o meno:

    • 0 - Installazione completata correttamente.

    • 3010 – L'installazione è riuscita; è necessario riavviare.

    • 1642 – L'installazione è stata annullata.

    • Tutti gli altri codici - L'installazione ha rilevato errori; per ulteriori dettagli, esaminare i file di log creati in% temp%.

  3. Annulla installazione. È possibile annullare l'installazione in qualsiasi momento tramite il metodo Abort per impostare i flag m_downloadAbort e m_ installAbort nella sezione MMIO. Questa operazione arresta il processo di installazione di .NET Framework 4.

Esempio di concatenatore

L'esempio seguente avvia e tiene traccia del processo di installazione .NET Framework 4 in modo invisibile all'utente mentre mostra l'avanzamento.

Nota di avvisoAttenzione

È necessario eseguire l'esempio come amministratore.

È possibile scaricare la soluzione Visual Studio completa per la sezione relativa alla concatenazione del programma di installazione .NET Framework 4 dalla Microsoft Code Gallery.

Nelle sezioni seguenti vengono descritti i file più significativi in questo esempio.

MmIoChainer.h

  • Il file MmIoChainer.h contiene la definizione della struttura dei dati e la classe di base dalla quale deve essere derivata la classe del concatenatore. La struttura dei dati MMIO è costituita dal codice seguente.

    // 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 struttura della classe segue la struttura dei dati per l'implementazione di un concatenatore. Derivare la classe del server dalla classe MmioChainer per concatenare .NET Framework 4 ridistribuibile. La classe MmioChainerBase viene utilizzata sia dal concatenatore che dall'elemento concatenato. Nel codice seguente, metodi e membri sono stati modificati per fare in modo che tale esempio resti breve.

    // 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
            { 
    ...
            }
    
    ...
        };
    
  • Il concatenatore viene implementato nel modo seguente.

    // 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'elemento concatenato deriva dalla stessa classe di 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

  • È possibile derivare dalla classe MmioChainer ed eseguire l'override dei metodi appropriati per visualizzare informazioni sullo stato di avanzamento. Notare che MmioChainer già fornisce un metodo di blocco di Run() che la classe derivata chiamerà. La classe Server nel codice seguente avvia il programma di installazione specificato, monitora lo stato di avanzamento e restituisce un codice di uscita.

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

    I dati dello stato di avanzamento saranno un char senza segno compreso tra 0 (0%) e 255 (100%). L'output dal metodo Finished è un HRESULT.

    Nota importanteImportante

    .NET Framework 4 ridistribuibile in genere scrive molti messaggi dello stato di avanzamento e un messaggio singolo che indica il completamento (dal lato del concatenatore).Legge anche in modo asincrono, cercando record Abort.Se riceve un record Abort, annulla l'installazione e (alla fine) scrive un record finito con E_ABORT come dati.

    Un server tipico crea un nome file MMIO casuale, crea il file (come mostrato nell'esempio di codice precedente, in Server::CreateSection) e avvia il ridistribuibile tramite CreateProcess, passando il nome pipe con l'opzione "-pipe someFileSectionName". I metodi del server OnProgress e Finished contengono codice specifico del server.

IprogressObserver.h

  • L'osservatore dello stato di avanzamento riceve una notifica di avanzamento (0-255) e di completamento dell'installazione.

    #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
    
  • HRESULT viene passato al metodo Finished. Il seguente codice mostra il punto di ingresso principale al programma.

    // 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);
    }
    
  • Modificare il percorso del file eseguibile (Setup.exe nell'esempio) per puntare al percorso corretto o modificare il codice per determinarlo. È necessario eseguire il codice come amministratore.

Vedere anche

Concetti

Distribuzione di .NET Framework e delle applicazioni

Altre risorse

Guida alla distribuzione di .NET Framework per sviluppatori

Cronologia delle modifiche

Data

Cronologia

Motivo

Luglio 2010

Argomento aggiunto.

Miglioramento delle informazioni.