本篇我們以一個Sample工程,來說明如何把一個常見結構的desktop application,轉制成APPX並在MS Store提供下載。
之前的篇章中,我們已經介紹了一些內容,包括如何通過Visual Studio創建Packaging工程, 如何將Class Libraries轉換到.NET Standard版本。至此,UI界面,以及部分DLL的遷移問題得到解決。但有時候我們的desktop application還包括一個本地的Background Service。那么目前微軟推薦的策略,依然是通過創建一個WCF服務,然后host在Windows Service上,來提供給前端的APPX訪問。
可能有同學要問,為什么我們需要Windows Service。這是因為在前台程序中,一般不會要求admin權限,這在企業級軟件中是較為常見的需求,用PowerPiont的普通員工,通常被認為即沒有power也沒有point,更不需要admin權限……(悲劇啊T_T)但我們可以把需要admin權限,訪問硬件等相關操作,放到Service中,由IT統一部署到機器上,避免了沒有admin權限無法正常運行程序的問題。
在接下來的Sample中,我們試圖通過代碼啟動一個Windows服務"aspnet_state"。這個工程非常的簡單,我們要做的僅僅是在UWP project中調用一下Windonws Service上運行的WCF服務。工程結構如下圖。因為工程主要想說明UI程序對WCF服務的引用,所以請不要在意使用UI程序是UWP還是WPF。實際使用中,對於Desktop Bridge的程序,非WCF的其他Background Service也是可以的,比如Named Pipes等。
我們的UI部分非常的簡單,僅是在MainPage.cs中調用WCF服務中提供的方法,這里我們可以看到傳遞了一個string類型的參數作為要啟動的service name。
public sealed partial class MainPage : Page { private string serviceName = "aspnet_state"; private LocalServiceClient client = new LocalServiceClient(); public MainPage() { this.InitializeComponent(); } private async void Button_Click(object sender, RoutedEventArgs e) { var status = await client.StartServiceAsync(serviceName); textBlockStatus.Text = status.ToString(); } }
如何編寫WCF服務這里就不贅述了,有興趣的同學可以去MSDN上學習:
https://docs.microsoft.com/en-us/dotnet/framework/wcf/index
同樣直接給出如何將WCF服務host在Windows Service上的鏈接:
https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-host-a-wcf-service-in-a-managed-windows-service
在WCF service的代碼里,我們通過ServiceController啟動另一個系統的Windows Service,需要注意的是,我們測試用的"aspnet_state"默認是未開啟的,而啟動Windows Service要求程序具有admin權限。
public class LocalServiceWrapper : ILocalService { public ServiceControllerStatus StartService(string name) { ServiceController controller = new ServiceController(name); if (controller.Status == ServiceControllerStatus.Stopped) { controller.Start(); controller.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(5)); } return controller.Status; } }
你也可以試試Bluetooth Support Service,Service name是"bthserv"。通過程序開啟藍牙服務似乎更合理,但反復測試disable/enable藍牙讓我很煩躁。所以最終我換成了"aspnet_state"。
把程序拆分成UI和WCF Service兩部分的目的,是將需要admin權限等不符合MS Store審核要求的代碼,從APPX中移出,已servcie的形式來調用。最終的結構圖可以分為兩種。
第一種是采用Desktop Bridge的形式,APPX部分可以繼續包含native C++ libraries,也可以調用任何形式的background service。唯一的問題是,在提交APPX到商店審核的時候,需要申請相應權限。企業級的軟件通過審核的可能性很大,個人作品就不清楚了。審核通過后APPX獲得微軟的簽名。此時用戶可從商店下載APPX,然后手動安裝Service部分。也可以由公司IT以Sideload的方式統一安裝APPX+Service。
第二種更為純粹,由UWP+.NET Standard+WCF組成,相應也需要更多的改動,而Native C++ Libraries的部分則需要下沉到Service中。這種的好處在於遷移的更為徹底,對未來有着更好的適應。當然哪天某軟翻臉不認賬,拋棄UWP又回頭去搞.NET Core的WPF的話,你就當我沒說過……
本篇討論了如何將現有的desktop application,在維持已有架構的前提下,轉制成APPX放到MS Store提供下載。
當然你們會覺得多出來的Service部分簡直不能忍。后續我會進一步介紹如何處理Service部分,不至於讓強迫症患者在評論里罵我……
本篇Sample工程的GitHub鏈接:
https://github.com/manupstairs/UWPWithWCFSample