MS Edge: In-process native messaging

0

Alongside reading this documentation I am trying to implement an Edge-Browser extension for native messaging to invoke a standard desktop application using Windows.System.Launcher.LaunchFileAsync.

I am explicitly not trying to implement the extension by an AppService using a Background-Task, as I need to make sure LaunchFileAsync is being executed on the Main UI-Thread. Hence, I want to use OnBackgroundActiviated on the main app component directly.

The documentation mentioned above says that turning an app-service based extension into an in-process extension is done by removing the "EntryPoint" attribute from the application tag in the package manifest. However, following this description, Visual Studio 2017 throws an error that the extension could not be registered (DEP0700, 0x80073CF6 windows.appService: Element not found).

Adding an EntryPoint attribute to the AppService Tag results in this error message disappearing, but I can't get OnBackgroundActivated to fire.

Here is my package manifest:

    <?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" IgnorableNamespaces="uap mp">
  <Identity Name="0d2a700c-9f88-4445-8250-a3e82aedb1d2" Publisher="CN=Sebastian Blessing" Version="1.0.0.0" />
  <mp:PhoneIdentity PhoneProductId="0d2a700c-9f88-4445-8250-a3e82aedb1d2" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
  <Properties>
    <DisplayName>edgeextension</DisplayName>
    <PublisherDisplayName>Sebastian Blessing</PublisherDisplayName>
    <Logo>Assets\StoreLogo.png</Logo>
  </Properties>
  <Dependencies>
    <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
  </Dependencies>
  <Resources>
    <Resource Language="x-generate" />
  </Resources>
  <Applications>
    <Application Id="App" Executable="$targetnametoken$.exe">
      <uap:VisualElements AppListEntry="none" DisplayName="edgeextension" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="abs.mira.edgeextension" BackgroundColor="transparent">
        <uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png">
        </uap:DefaultTile>
        <uap:SplashScreen Image="Assets\SplashScreen.png" />
      </uap:VisualElements>
      <Extensions>
        <uap:Extension Category="windows.appService">
          <uap:AppService Name="EdgeExtensionRuntime" />
        </uap:Extension>
        <uap3:Extension Category="windows.appExtension">
          <uap3:AppExtension Name="com.microsoft.edge.extension" Id="EdgeExtensionRuntime" PublicFolder="Extension" DisplayName="ms-resource:DisplayName">
            <uap3:Properties>
              <Capabilities>
                <Capability Name="browserStorage" />
              </Capabilities>
            </uap3:Properties>
          </uap3:AppExtension>
        </uap3:Extension>
      </Extensions>
    </Application>
  </Applications>
  <Capabilities>
    <Capability Name="internetClient" />
    <Capability Name="internetClientServer" />
  </Capabilities>
</Package>

Can you point me to a potential issue of this manifest file? The application Class is implemented as App in File App.xaml.cs and also implements OnBackgroundActivated, and here is the code:

using System;
using Windows.ApplicationModel;
using Windows.Foundation.Collections;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.AppService;
using Windows.ApplicationModel.Background;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using Newtonsoft.Json;

namespace edgeextension
{
    sealed partial class App : Application
    {
        private BackgroundTaskDeferral appServiceDeferral = null;
        private AppServiceConnection appServiceConnection;

        public App()
        {
            this.InitializeComponent();
            this.Suspending += OnSuspending;
        }

        protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
        {
            base.OnBackgroundActivated(args);
            IBackgroundTaskInstance taskInstance = args.TaskInstance;
            AppServiceTriggerDetails appService = taskInstance.TriggerDetails as AppServiceTriggerDetails;
            appServiceDeferral = taskInstance.GetDeferral();
            taskInstance.Canceled += OnAppServicesCanceled;
            appServiceConnection = appService.AppServiceConnection;
            appServiceConnection.RequestReceived += OnAppServiceRequestReceived;
            appServiceConnection.ServiceClosed += AppServiceConnection_ServiceClosed;
        }

        private async void OnAppServiceRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
        {
            AppServiceDeferral messageDeferral = args.GetDeferral();

            try
            {
                //Has to run on the UI-Thread !!                       
                await Windows.System.Launcher.LaunchFileAsync(...);
            }
            finally
            {
                messageDeferral.Complete();
            }
        }

        private void OnAppServicesCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
        {
            this.appServiceDeferral.Complete();

        }

        private void AppServiceConnection_ServiceClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
        {
            this.appServiceDeferral.Complete();
        }

        void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
        }

        private void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();
            //TODO
            //deferral.Complete();
        }
    }
}

In the background script, I connect to the extension using the same name EdgeExtensionRuntime as in the manifest file:

// Store port for native messaging connections
var port = null;

browser.runtime.onMessage.addListener(
    function (request, sender, sendResponse) {
        if (port === null) {
            port = browser.runtime.connectNative("EdgeExtensionRuntime");
            port.onDisconnect.addListener(onDisconnected);

            port.onMessage.addListener(function (response) { console.log(response); });
        }

        try {
            port.postMessage(request.message);
        } catch (e) {
            console.log(e);
        }


        return true;
    });

function onDisconnected() {
    console.log("disconnect!");
    port = null;
}

Apparently, this doesn't appear to work (even with an EntryPoint added to the extension-tag), since I do get an immediate "disconnect" in the extensions background-page log.

I can't see what the issue is, and the MS documentation on this is very poor.

c#
visual-studio
uwp
microsoft-edge
microsoft-edge-extension
asked on Stack Overflow Jan 2, 2018 by Sebastian B. • edited Feb 5, 2018 by TylerH

1 Answer

1

The solution that causes OnBackgroundActivated to fire is to add the following protocol information to the package manifest:

<uap:Extension Category="windows.protocol">
  <uap:Protocol Name="msghost1" />
</uap:Extension>

However, having now converted the application to an in-process plugin does not solve the issue of Windows.System.Launcher.LaunchFileAsync(IStorageFile) to run on the main-thread. So far, this call only succeeds in Debug Mode (with the debugger attached). Will post a separate question for this issue.

answered on Stack Overflow Jan 3, 2018 by Sebastian B.

User contributions licensed under CC BY-SA 3.0