Bagikan melalui


Cara: Mendapatkan Kemajuan dari Alat Penginstal .NET Framework 4.5

.NET Framework 4.5 adalah runtime yang dapat didistribusikan ulang. Jika Anda mengembangkan aplikasi untuk versi .NET Framework ini, Anda dapat menyertakan (rantai) penyiapan .NET Framework 4.5 sebagai bagian prasyarat dari penyiapan aplikasi Anda. Untuk menyajikan pengalaman penyiapan yang disesuaikan atau terpadu, Anda mungkin ingin meluncurkan penyiapan .NET Framework 4.5 dalam mode senyap dan melacak progresnya sambil menunjukkan progres penyiapan aplikasi Anda. Untuk mengaktifkan pelacakan senyap, penyiapan .NET Framework 4.5 (yang dapat ditonton) menetapkan protokol dengan menggunakan segmen I/O (MMIO) yang dipetakan memori untuk berkomunikasi dengan penyiapan Anda (pengamat atau rantai). Protokol ini menentukan cara bagi chainer untuk mendapatkan informasi progres, mendapatkan hasil mendetail, merespons pesan, dan membatalkan penyiapan .NET Framework 4.5.

  • Invokasi. Untuk memanggil penyiapan .NET Framework 4.5 dan menerima informasi progres dari bagian MMIO, program penyiapan Anda harus melakukan hal berikut:

    1. Panggil program .NET Framework 4.5 yang dapat didistribusikan ulang:

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

      Di mana nama bagian adalah nama yang ingin Anda gunakan untuk mengidentifikasi aplikasi Anda. Penyiapan .NET Framework membaca dan menulis ke bagian MMIO secara asinkron, sehingga Anda mungkin merasa nyaman menggunakan peristiwa dan pesan selama waktu tersebut. Dalam contoh, proses penyiapan .NET Framework dibuat oleh konstruktor yang mengalokasikan bagian MMIO (TheSectionName) dan mendefinisikan peristiwa (TheEventName):

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

      Harap ganti nama tersebut dengan nama yang unik untuk program penyiapan Anda.

    2. Baca dari bagian MMIO. Di .NET Framework 4.5, operasi pengunduhan dan penginstalan bersifat bersamaan: Salah satu bagian dari .NET Framework mungkin menginstal saat bagian lain sedang mengunduh. Akibatnya, progres dikirim kembali (yaitu, tertulis) ke bagian MMIO sebagai dua angka (m_downloadSoFar dan m_installSoFar) yang meningkat dari 0 menjadi 255. Ketika 255 ditulis dan .NET Framework keluar, penginstalan selesai.

  • Kode keluar. Kode keluar berikut dari perintah untuk memanggil program .NET Framework 4.5 yang dapat didistribusikan ulang menunjukkan apakah penyiapan telah berhasil atau gagal:

    • 0 - Penyiapan berhasil diselesaikan.

    • 3010 – Penyiapan berhasil diselesaikan; restart sistem diperlukan.

    • 1602 – Penyiapan telah dibatalkan.

    • Semua kode lainnya - Penyiapan menemukan kesalahan; periksa file log yang dibuat dalam %temp% untuk detailnya.

  • Membatalkan penyiapan. Anda dapat membatalkan penyiapan kapan saja dengan menggunakan metode Abort untuk mengatur bendera m_downloadAbort dan m_ installAbort di bagian MMIO.

Contoh Chainer

Sampel Chainer secara diam-diam meluncurkan dan melacak penyiapan .NET Framework 4.5 sambil menunjukkan progres. Sampel ini mirip dengan sampel Chainer yang disediakan untuk .NET Framework 4. Namun, selain itu, sampel ini dapat menghindari restart sistem dengan memproses kotak pesan untuk menutup aplikasi .NET Framework 4. Untuk informasi tentang kotak pesan ini, lihat Mengurangi Restart Sistem Selama Penginstalan .NET Framework 4.5. Anda dapat menggunakan contoh ini dengan penginstal .NET Framework 4; dalam skenario tersebut, pesan tidak terkirim.

Peringatan

Anda harus menjalankan contoh sebagai administrator.

Bagian berikut menjelaskan file penting dalam contoh ini: MMIOChainer.h, ChainingdotNet4.cpp, dan IProgressObserver.h.

MMIOChainer.h

  • File MMIOChainer.h berisi definisi struktur data dan kelas dasar dari mana kelas chainer harus diturunkan. .NET Framework 4.5 memperluas struktur data MMIO untuk menangani data yang dibutuhkan oleh penginstal .NET Framework 4.5. Perubahan pada struktur MMIO kompatibel untuk versi sebelumnya, sehingga rantai .NET Framework 4 dapat bekerja dengan penyiapan .NET Framework 4.5 tanpa memerlukan kompilasi ulang. Namun, skenario ini tidak mendukung fitur untuk mengurangi restart sistem.

    Bidang versi menyediakan sarana untuk mengidentifikasi revisi struktur dan format pesan. Penyiapan .NET Framework menentukan versi antarmuka rantai dengan memanggil fungsi VirtualQuery untuk menentukan ukuran pemetaan file. Jika ukurannya cukup besar untuk mengakomodasi bidang versi, penyiapan .NET Framework menggunakan nilai yang ditentukan. Jika pemetaan file terlalu kecil untuk memuat bidang versi, yang merupakan kasus dengan .NET Framework 4, proses penyiapan mengasumsikan versi 0 (4). Jika rantai tidak mendukung versi pesan yang ingin dikirim oleh penyiapan .NET Framework, penyiapan .NET Framework mengasumsikan respons abaikan.

    Struktur data MMIO didefinisikan sebagai berikut:

    // 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.
        };
    
  • Struktur data MmioDataStructure tidak boleh digunakan secara langsung; gunakan kelas MmioChainer sebagai gantinya untuk menerapkan chainer Anda. Berasal dari kelas MmioChainer untuk menautkan .NET Framework 4.5 yang dapat didistribusikan ulang.

IProgressObserver.h

  • File IProgressObserver.h menerapkan pengamat progres. Pengamat ini mendapat pemberitahuan tentang progres pengunduhan dan penginstalan (ditetapkan sebagai char yang tidak ditandatangani, 0-255, menunjukkan 1% -100% selesai). Pengamat juga diberitahu ketika chainee mengirim pesan, dan pengamat harus mengirim respons.

        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

  • File ChainingdotNet4.5.cpp menerapkan kelas Server, yang diturunkan dari kelas MmioChainer dan menggantikan metode yang sesuai untuk menampilkan informasi progres. MmioChainer membuat bagian dengan nama bagian yang ditentukan dan menginisialisasi rantai dengan nama peristiwa yang ditentukan. Nama peristiwa disimpan dalam struktur data yang dipetakan. Anda harus membuat bagian dan nama peristiwa yang unik. Kelas Server dalam kode berikut meluncurkan program penyiapan yang ditentukan, memantau progresnya, dan mengembalikan kode keluar.

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

    Instalasi dimulai dalam metode Utama.

    // 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;
    }
    
  • Sebelum meluncurkan instalasi, chainer memeriksa untuk melihat apakah .NET Framework 4.5 sudah diinstal dengan memanggil 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));
    }
    
  • Anda dapat mengubah jalur executable (Setup.exe dalam contoh) dalam metode Launch untuk menunjuk ke lokasi yang benar, atau menyesuaikan kode untuk menentukan lokasi. Kelas dasar MmioChainer menyediakan metode Run() pemblokiran yang dipanggil oleh kelas turunan.

    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);
    }
    
  • Metode Send memotong dan memproses pesan. Dalam versi .NET Framework ini, satu-satunya pesan yang didukung adalah pesan tutup aplikasi.

            // 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;
        }
    };
    
  • Data progres adalah char yang tidak ditandatangani antara 0 (0%) dan 255 (100%).

    private: // IProgressObserver
        virtual void OnProgress(unsigned char ubProgressSoFar)
        {…………
       }
    
  • HRESULT diteruskan ke metode Finished.

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

    Penting

    .NET Framework 4.5 yang dapat didistribusikan ulang biasanya menulis banyak pesan progres dan satu pesan yang menunjukkan penyelesaian (di sisi rantai). Ini juga membaca secara tidak sinkron, mencari catatan Abort. Jika menerima catatan Abort, ini akan membatalkan penginstalan, dan menulis catatan yang sudah selesai dengan E_ABORT sebagai datanya setelah penginstalan dibatalkan dan operasi penyiapan dibatalkan.

Server umum membuat nama file MMIO acak, membuat file (seperti yang ditunjukkan pada contoh kode sebelumnya, di Server::CreateSection), dan meluncurkan yang dapat didistribusikan ulang dengan menggunakan metode CreateProcess dan meneruskan nama pipa dengan opsi -pipe someFileSectionName. Server harus menerapkan metode OnProgress, Send, dan Finished dengan kode khusus UI aplikasi.

Lihat juga