At previous article, I wrote a limitation to use System.IO.Ports.SerialPort class on UWP.
How to access virtual COM port from UWP app
But, there is a limitation. SerialPort class on UWP can't list ports using GetPortNames method.
If the method was used, then PlatformNotSupportedException would be occurred.
GetPortNames method can't call on UWP. But, the method is available on .NET Framework.
Yes, UWP app can launch a full trust process that is in same package, and then UWP can communicate to the full trust process.
At DesktopBridgeToUWP-Samples repository, there is a sample program about this.
I thought that using GetPortNames method is good example. So, I created a sample program that is changed previous article's sample.
Key technologies
Windows Application Package Project(MSIX)
First, we can create a msix package file that includes a UWP app and a Win32 app using Windows Application Package Project.
The following picture is solution explorer of the sample app.
App Services
App Services(It is not Azure service!) is one of UWP features to provide functions to other apps from UWP.
Use app services and extensions
In this sample app, the feature is used to communicate between the UWP app and the Win32 app.
UWP app side is hosting App Service, and Win32 app connect to it, and then they can send / receive messages together using AppServiceConnection class.
Of course, you can choice other methods to communicate between UWP app and Win32 app like ApplicationData.Current.LocalSettings.
Using Windows 10 APIs from .NET Framework app
You can use Windows 10 APIs from .NET Framework app. It is written on following document:
There are a few steps, however we can do that more easier using Microsoft.Windows.SDK.Contracts NuGet package. In this sample, I'm using the package.
There is just one step. it is to add the package to .NET Framework project.
Launch full trues process from UWP app
There is FullTrustProcessLauncher Class that is to launch full trust win32 app from UWP app. It is provided through Windows Desktop Extension SDK.
If you want to use it, please add the reference to UWP project.
Let's get list of port names.
Base of sample program is a sample program of a previous article.
The repository is here: https://github.com/runceel/VirtualCOMPortApp
I added two projects to the solution.
To launch the win32 app as full trust process, I added desktop:Extension tag to Package.appxmanifest of VirtualCOMPortApp.Package project, like below:
<Extensions>
<desktop:Extension Category="windows.fullTrustProcess" Executable="VirtualCOMPortApp.Win32\VirtualCOMPortApp.Win32.exe" />
</Extensions>
And to use app service, I added an uap:Extension tag to Package.appxmanifest of VirtualCOMPortApp.Package project, like below:
<uap:Extension Category="windows.appService">
<uap:AppService Name="InProcessAppService" />
</uap:Extension>
Add implementation to get port names to VirtualCOMPortApp.Win32, and communicate to UWP process using AppServiceConnection.
To connect the app service:
var conn = new AppServiceConnection(); conn.PackageFamilyName = Package.Current.Id.FamilyName; conn.AppServiceName = "InProcessAppService"; conn.RequestReceived += Conn_RequestReceived; conn.ServiceClosed += Conn_ServiceClosed; await conn.OpenAsync();
Implements RequestReceived event handler to communicate to UWP process:
private static async void Conn_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { var d = args.GetDeferral(); var requestMessage = args.Request.Message; if (requestMessage.TryGetValue("command", out var commandName)) { switch((string)commandName) { case "GetPortNames": { await args.Request.SendResponseAsync(new ValueSet { ["portNames"] = JsonConvert.SerializeObject(SerialPort.GetPortNames()), }); break; } default: // noop break; } } d.Complete(); }
And then I created a wrapper class to call the full truest process from UWP, like below:
... namespace VirtualCOMPortApp { public static class SerialPortService { private static AppServiceConnection _appServiceConnection; private static BackgroundTaskDeferral _backgroundTaskDeferral; private static readonly TaskCompletionSource<object> _initialized = new TaskCompletionSource<object>(); private static Task WaitForInitializedAsync() => _initialized.Task; // launch the full trust process public static Task LaunchWin32AppAsync() => FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync().AsTask(); // set up app service public static void AcceptAppServiceConnection(IBackgroundTaskInstance instance) { instance.Canceled += AppServiceCanceled; var appService = (AppServiceTriggerDetails)instance.TriggerDetails; _backgroundTaskDeferral = instance.GetDeferral(); _appServiceConnection = appService.AppServiceConnection; _appServiceConnection.ServiceClosed += AppServiceConnection_ServiceClosed; _initialized.SetResult(null); } // get port names using AppServiceConnection public static async Task<string[]> GetPortNamesAsync() { await WaitForInitializedAsync(); var response = await _appServiceConnection.SendMessageAsync(new ValueSet { ["command"] = "GetPortNames", }); if (response.Status != AppServiceResponseStatus.Success) { throw new InvalidOperationException($"{response.Status}"); } return JsonConvert.DeserializeObject<string[]>(response.Message["portNames"] as string ?? "[]"); } // closing process private static void AppServiceCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason) => _backgroundTaskDeferral.Complete(); private static void AppServiceConnection_ServiceClosed(AppServiceConnection sender, AppServiceClosedEventArgs args) => _backgroundTaskDeferral.Complete(); } }
And to launch the full trust process when UWP app was launched, so, let's add following code at OnLaunched method of App class.
protected override void OnLaunched(LaunchActivatedEventArgs e) { ... if (rootFrame == null) { _ = SerialPortService.LaunchWin32AppAsync(); // Launch the full trust process // Create a Frame to act as the navigation context and navigate to the first page rootFrame = new Frame(); ... } ... }
At finally, implements OnBackgroundActivated method of App class, like below:
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) { base.OnBackgroundActivated(args); if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails) { // set up app service SerialPortService.AcceptAppServiceConnection(args.TaskInstance); } }
OK, all preparing process is finished!! Create a page using SeralPortService class.
It is same as common programming of UWP. if you interested about that, then please see following github repository's using-getportnames branch.
https://github.com/runceel/VirtualCOMPortApp/tree/using-getportnames
The app's result is as below:
The app can get list of port names!
Wrap up
UWP apps have some limitations like getting port names of virtual COM port.
In those case, there are something to be able to resolve issues using Desktop Bridge technology written in this article.
Thank you.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.