Get a list of virtual serial COM ports from UWP app
Published Jul 02 2019 12:40 AM 8,106 Views
Microsoft

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.

AppService Bridge Sample

 

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.

コメント 2019-06-27 125811.jpg

  • VirtualCOMPortApp (Universal Windows)
    This is a UWP app project. It communicates to VirtualCOMPortApp.Win32 to call GetPortNames method.
  • VirtualCOMPortApp.Package
    This is a Windows Application Package Project. This is to create a msix package file that includes the UWP app and the Win32 app.
  • VirtualCOMPortApp.Win32
    This is a Console App(.NET Framework). This is to call GetPortNames method.

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:

Call UWP APIs in desktop apps

 

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.

コメント 2019-06-27 131942.jpg

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.

コメント 2019-07-02 143937.jpg

  • VirtualCOMPortApp.Package project
    This is a Windows Application Package project. I added VirtualCOMPortApp project and VirtualCOMPortApp.Win32 project to Applications node. And then set VirtualCOMPortApp as entry point.
  • VirtualCOMPortApp.Win32 project
    This is a Console App(.NET Framework) project. I added Microsoft.Windows.SDK.Contracts NuGet package to this.

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:

vcom.gif

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.

4 Comments
Version history
Last update:
‎Sep 13 2019 04:18 AM
Updated by: