简体   繁体   中英

Can't get RemoteSystem to work in UWP App

I am trying to implement Project Rome functionality into my UWP App by making the device(s) discoverable and remote session(s) joined from different remote systems/devices.

Overview on Project Rome can be found here: Project Rome

What I am trying to achieve in a broad sense is described here: Connect devices through remote sessions

I have followed the code and downloaded the Quiz sample App provided on GitHub here: Remote System session - Quiz game App

My code is below. I am using simplified code to get the basic functionality working:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Windows.System.RemoteSystems;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace App3
{
    public sealed partial class MainPage : Page, INotifyPropertyChanged
    {
        private RemoteSystemSessionWatcher m_sessionWatcher;
        private RemoteSystemSessionController m_sessionController;

        private string _statusText;
        public string StatusText
        {
            get => _statusText;
            set => SetProperty(ref _statusText, value);
        }

        public MainPage()
        {
            this.InitializeComponent();

            if (!RemoteSystem.IsAuthorizationKindEnabled(RemoteSystemAuthorizationKind.Anonymous))
            {
                // The system is not authorized to connect to cross-user devices. Inform the user that they can discover more devices if they
                // update the setting to "Everyone nearby".
                StatusText = $"The system is not authorized to connect to cross-user devices!";
            }
            else
            {
                StatusText = $"Initialisation OK!";
            }
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            if (m_sessionController != null)
            {
                m_sessionController.JoinRequested -= RemoteSystemSessionController_JoinRequested;
                m_sessionController = null;
            }

            if (m_sessionWatcher != null)
            {
                m_sessionWatcher.Stop();
                m_sessionWatcher.Added -= RemoteSystemSessionWatcher_RemoteSessionAdded;
                m_sessionWatcher.Removed -= RemoteSystemSessionWatcher_RemoteSessionRemoved;
                m_sessionWatcher = null;
            }
        }

        private async void CreateSession_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            StatusText = "";

            if (string.IsNullOrWhiteSpace(SessionNameBox.Text))
            {
                StatusText = $"Provide a Session name first!";
                return;
            }

            await StartNewSharedExperience(SessionNameBox.Text);
        }

        private async void JoinSession_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            SessionNameBox.Text = "";
            await DiscoverSessions();
        }

        public async Task StartNewSharedExperience(string sessionName)
        {
            RemoteSystemAccessStatus accessStatus = await RemoteSystem.RequestAccessAsync();

            // Checking that remote system access is allowed - ensure capability in the project manifest is set
            if (accessStatus != RemoteSystemAccessStatus.Allowed)
            {
                StatusText = $"Create Session: Access is denied, ensure the 'bluetooth' and 'remoteSystem' capabilities are set.";
                return;
            }

            m_sessionController = new RemoteSystemSessionController(sessionName);

            // register the following code to handle the JoinRequested event
            m_sessionController.JoinRequested += RemoteSystemSessionController_JoinRequested;

            // create and start the session
            RemoteSystemSessionCreationResult createResult = await m_sessionController.CreateSessionAsync();

            // handle the creation result
            if (createResult.Status == RemoteSystemSessionCreationStatus.Success)
            {
                // creation was successful
                RemoteSystemSession currentSession = createResult.Session;

                // optionally subscribe to the disconnection event
                currentSession.Disconnected += async (sender, args) => {
                    // update the UI, using args.Reason
                    // ...
                    StatusText = $"Session disconnected. Reason: {args.Reason.ToString()}";
                };

                // Use session ...
                StatusText = $"Session '{sessionName}' created successfully.";
            }
            else if (createResult.Status == RemoteSystemSessionCreationStatus.SessionLimitsExceeded)
            {
                // creation failed. Optionally update UI to indicate that there are too many sessions in progress
                StatusText = $"Too many Sessions!";
            }
            else
            {
                // creation failed for an unknown reason. Optionally update UI
                StatusText = $"Creation failed with unknown reason!";
            }
        }

        // Discovery of existing sessions
        public async Task DiscoverSessions()
        {
            try
            {
                RemoteSystemAccessStatus status = await RemoteSystem.RequestAccessAsync();

                // Checking that remote system access is allowed - ensure capability in the project manifest is set
                if (status != RemoteSystemAccessStatus.Allowed)
                {
                    StatusText = $"Discover Session: Access is denied, ensure the 'bluetooth' and 'remoteSystem' capabilities are set.";
                    return;
                }

                //  Create watcher to observe for added, updated, or removed sessions
                m_sessionWatcher = RemoteSystemSession.CreateWatcher();
                m_sessionWatcher.Added += RemoteSystemSessionWatcher_RemoteSessionAdded;
                m_sessionWatcher.Removed += RemoteSystemSessionWatcher_RemoteSessionRemoved;
                m_sessionWatcher.Start();
                StatusText = $"Session discovery started.";
            }
            catch (Win32Exception ex)
            {
                StatusText = $"Session discovery failed. Exception occurred: {ex.Message}";
            }
        }

        private void RemoteSystemSessionController_JoinRequested(RemoteSystemSessionController sender, RemoteSystemSessionJoinRequestedEventArgs args)
        {
            // Get the deferral
            var deferral = args.GetDeferral();

            // display the participant (args.JoinRequest.Participant) on UI, giving the
            // user an opportunity to respond
            // ...
            StatusText = $"Session join requested by: {args.JoinRequest.Participant.RemoteSystem.DisplayName}";

            // If the user chooses "accept", accept this remote system as a participant
            args.JoinRequest.Accept();
            deferral.Complete();
        }

        private void RemoteSystemSessionWatcher_RemoteSessionAdded(RemoteSystemSessionWatcher sender, RemoteSystemSessionAddedEventArgs args)
        {
            StatusText = $"Discovered Session {args.SessionInfo.DisplayName}:{args.SessionInfo.ControllerDisplayName}.";
        }

        private void RemoteSystemSessionWatcher_RemoteSessionRemoved(RemoteSystemSessionWatcher sender, RemoteSystemSessionRemovedEventArgs args)
        {
            StatusText = $"Session {args.SessionInfo.DisplayName}:{args.SessionInfo.ControllerDisplayName} was ended by host.";
        }

        public bool SetProperty<T>(ref T field, T value, [CallerMemberName]string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(field, value))
            {
                return false;
            }

            field = value;
            OnPropertyChanged(propertyName);

            return true;
        }

        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

I have not changed the App.xml.cs file. There is only one (Main) page.

My XAML is as follows:

    <Page
    x:Class="App3.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App3"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,100,0,0">
            <TextBlock Text="Status:" FontSize="18" Foreground="Black" Margin="20,0,0,0"/>
            <TextBlock x:Name="StatusTextUI" FontSize="16" Foreground="Black" Text="{x:Bind StatusText, Mode=OneWay}" Margin="10,0,0,0"
                       VerticalAlignment="Center"/>
        </StackPanel>

        <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,40,0,0">
            <TextBlock Text="Session Name:" FontSize="18" Foreground="Black" HorizontalAlignment="Left" Margin="20,0,0,0" />
            <TextBox x:Name="SessionNameBox" Width="740" Margin="10,0,0,0"/>
            <Button x:Name="CreateSessionButton" HorizontalAlignment="Center" Content="Create Session" Margin="20,0,0,0"
                    Click="CreateSession_Click" />
        </StackPanel>

        <StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,40,0,0">
            <TextBlock Text="Session Discovered:" FontSize="18" Foreground="Black" HorizontalAlignment="Left" Margin="20,0,0,0" />
            <TextBlock x:Name="SessionFound" Width="700" Margin="10,0,0,0"/>
            <Button x:Name="JoinSessionButton" HorizontalAlignment="Center" Content="Join Session" Margin="20,0,0,0"
                    Click="JoinSession_Click"/>
        </StackPanel>
    </Grid>
</Page>

The App is using the latest libraries, see References screenshot below:

Visual Studio 2017 NuGet Package Manager details

My manifest code is:

    <?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 uap3">
  <Identity Name="7b15365b-4ve6-4c19-a0c6-0b47g8733b52" Publisher="CN=ANOtherCompany" Version="1.0.0.0" />
  <mp:PhoneIdentity PhoneProductId="7b15365b-4ve6-4c19-a0c6-0b47g8733b52" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
  <Properties>
    <DisplayName>App3</DisplayName>
    <PublisherDisplayName>ANOtherCompany</PublisherDisplayName>
    <Logo>Assets\StoreLogo.png</Logo>
  </Properties>
  <Dependencies>
    <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.16299.15" MaxVersionTested="10.0.16299.15" />
  </Dependencies>
  <Resources>
    <Resource Language="x-generate" />
  </Resources>
  <Applications>
    <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="App3.App">
      <uap:VisualElements DisplayName="App3" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="App3" BackgroundColor="transparent">
        <uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png">
        </uap:DefaultTile>
        <uap:SplashScreen Image="Assets\SplashScreen.png" />
      </uap:VisualElements>
    </Application>
  </Applications>
  <Capabilities>
    <Capability Name="internetClientServer" />
    <Capability Name="internetClient" />
    <Capability Name="privateNetworkClientServer" />
    <uap3:Capability Name="remoteSystem" />
    <DeviceCapability Name="bluetooth" />
  </Capabilities>
</Package>

I have deployed my App on 2 different Windows 10 Surface devices but I can't discover the session created on one device in the other one. The quiz App behaves as expected on both devices. But I want something simpler. Breakpoints at session discovery get hit on the Quiz App, but not in my App. The 'Capabilities' section in my manifest is the same as the Quiz App. The basic code listed above is more or less from the official examples in the links provided above. So they ought to work but I don't know what I am doing wrong.

I have tried to provide as much info as possible, but please do not hesitate to ask if you require more.

Any assistance/guidance will be greatly appreciated.

For anyone with similar issue and wanting an answer:

I was trying to host a Remote Session and discover other sessions - within the same UWP App to facilitate App-to-App messaging. For Host-client sessions, the Quiz App example works just fine (see link provided in my question). But, for many-to-many Session hosting, discovery and sending messaging to a single Participant, it was difficult to achieve (at least for me). In other words, my App hosts a Remote Sessions and joins others.

This is my understanding of the flow and how I resolved it:

  1. Create a Remote Session
  2. Discover Remote session
  3. Create a Message Channel for it
  4. Watch remote session participants (RemoteSystemSessionParticipantWatcher)
  5. Then send the message to a participant on corresponding Message Channel (created in step 3).

When a Message Channel is created by a device, it also adds itself as a Participant. So, I save a reference to this (Host) Participant (in step 4) and then send the message to it when required.

The crux of the matter is to save reference to the remote session message channel and the specific Participant of it - then send the message to that. It is a bit complex to understand at first as I found out. Documentation is available but unfortunately not for the scenario I wanted. Nonetheless, I got there eventually. Sorry, can't post the code as it is quite large now, but I am hoping that interested reader would find the working logic above to be useful.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM