遷移桌面程序到MS Store(8)——經過APPX下載Win32Component

在上一篇《遷移桌面程序到MS Store(7)——APPX + Service》中,咱們提到將desktop application拆分紅UI Client+Service兩部分。其中UI Client能夠經過Desktop Bridge技術Pacakage成APPX,上傳到MS Store以供下載,而Service則仍以傳統的desktop application安裝包形式提供。這樣勢必形成用戶安裝時的割裂感。本篇將就這個問題進行一些討論。git

 

首先咱們參照上圖的架構建立Sample Solution,其中包括充當UI的WPFClient,升級到.NET Standard的DownloadLib,以及打包用的UWPClientPackaging工程(請先暫時忽略UWPClient工程)。github

 

WPFClient只是一個空的Window,僅僅在Window Load的時候,詢問用戶是否下載文件。json

        private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            txtLog.AppendText($"Ask user to download file.\n");
            var result = MessageBox.Show("We need to download file.", "Download", MessageBoxButton.YesNo);
            if (result == MessageBoxResult.Yes)
            {
                txtLog.AppendText($"Start downloading.\n");
                this.progressBar.Visibility = Visibility.Visible;
                var downloader = new SimpleDownloader();
                var stream = await downloader.RequestHttpContentAsync(
                    ConfigurationManager.AppSettings["uri"],
                    ConfigurationManager.AppSettings["username"],
                    ConfigurationManager.AppSettings["password"]);

                this.progressBar.Visibility = Visibility.Collapsed;
                txtLog.AppendText($"Done.\n");

                var path = SaveFile(stream);
                txtLog.AppendText($"File path is {path}.\n");

                Process.Start(path);
                txtLog.AppendText($"Start process {path}.\n");
            }
        }

這裏須要注意的是,代碼中的uri,username和password均寫在配置文件App.config中,調試時記得填寫真實的值。windows

  <appSettings>
    <add key="uri" value=""/>
    <add key="username" value=""/>
    <add key="password" value=""/>
  </appSettings>

DownloadLib工程在這個例子中充當了Class Library的角色,考慮到會被WPFClient和UWPClient同時調用,DownloadLib的項目類型是.NET Standard。該工程的代碼也很簡單,經過傳入的uri,username和password進行http請求下載文件,以Stream的形式返回結果。api

        public async Task<Stream> RequestHttpContentAsync(string uriString, string userName, string password)
        {
            using (HttpClient client = new HttpClient())
            {
                client.BaseAddress = new Uri(uriString);
                client.DefaultRequestHeaders.Accept.Clear();
                var authorization = Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes($"{userName}:{password}"));
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authorization);
                client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

                HttpResponseMessage response = await client.GetAsync(uriString);
                if (response.IsSuccessStatusCode)
                {
                    HttpContent content = response.Content;
                    var contentStream = await content.ReadAsStreamAsync();
                    return contentStream;
                }
                else
                {
                    throw new FileNotFoundException();
                }
            }
        }

假設咱們這裏下載的文件是一個.msi的安裝文件,這個安裝文件便是架構圖中Service部分的安裝包。在完成下載後,在WPFClient中將Stream保存成文件,而後經過Process.Start(path);運行,接下來就是.msi文件的安裝流程了。在安裝結束後,整個application就能夠正常使用了。安全

        private string SaveFile(Stream stream)
        {
            var filePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "installFile.msi");
            using (var fileStream = new FileStream(filePath, FileMode.Create))
            {
                byte[] buffer = new byte[2048];
                int bytesRead;
                do
                {
                    bytesRead = stream.Read(buffer, 0, 2048);
                    fileStream.Write(buffer, 0, bytesRead);
                } while (bytesRead > 0);

            }

            return filePath;
        }

WPFClient最終是經過UWPClientPackaging工程打包成爲APPX,實際對File和Process的操做都是標準的WPF代碼。不存在權限的問題。固然若是當前用戶沒有admin權限,不被容許安裝任何軟件,這就超出了咱們討論的範圍。
接下來咱們來看純UWPClient的狀況。UWPClient工程一樣添加了對DownloadLib的引用,界面也基本一致。稍有不一樣之處在於不能使用System.IO.File對象,而是要經過StorageFolder來保存文件。一樣也不可以使用Process.Start()方法,而是經過Launcher對象來打開保存文件所在的文件夾。某軟出於安全角度,不容許Launcher對象運行exe,msi等類型的可執行文件。因此只能打開文件夾讓用戶手動點擊安裝。
https://docs.microsoft.com/en-us/uwp/api/windows.system.launcher.launchfileasync
This API also imposes several restrictions on what types of files it can launch. Many file types that contain executable code, for example .exe, .msi, and .js files, are blocked from launching. This restriction protects users from potentially malicious files that could modify the system.架構

        private async void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            txtLog.Text += $"Ask user to download file.\n";
            var dialog = new MessageDialog("Do you want to download installation file?");
            dialog.Commands.Add(new UICommand { Label = "Ok", Id = 0 });
            dialog.Commands.Add(new UICommand { Label = "Cancel", Id = 1 });
            var res = await dialog.ShowAsync();

            if ((int)res.Id == 0)
            {
                txtLog.Text += $"Start downloading.\n";
                this.progressRing.IsActive = true;
                var downloader = new SimpleDownloader();
                var stream = await downloader.RequestHttpContentAsync(
                    "",
                    "",
                    "");

                this.progressRing.IsActive = false;
                var file = await SaveStorageFile(stream);
                var result = await Launcher.LaunchFolderAsync(ApplicationData.Current.LocalFolder);
                txtLog.Text += $"Done.\n ";
            }
        }

本篇討論瞭如何在APPX中下載文件並運行,使用戶僅須要在MS Store中進行一次下載便可完成整個application的安裝。實際使用中更適合經過Desktop Bridge打包的desktop application。而對純UWP的客戶端並不友好。對純UWP客戶端的處理咱們在下一篇中作更進一步討論。
GitHub:
https://github.com/manupstairs/AppxDownloadWin32Componentapp

相關文章
相關標籤/搜索