透過遠端工作階段連接裝置Connect devices through remote sessions

「遠端工作階段」功能可讓應用程式透過工作階段連接到其他裝置,以進行明確的應用程式傳訊,或進行系統管理資料代理交換,例如在 Windows 全像攝影版裝置之間用於全像攝影共用的 SpatialEntityStoreThe Remote Sessions feature allows an app to connect to other devices through a session, either for explicit app messaging or for brokered exchange of system-managed data, such as the SpatialEntityStore for holographic sharing between Windows Holographic devices.

任何 Windows 裝置都可以建立遠端工作階段,而且任何 Windows 裝置都可以要求加入其中 (雖然工作階段可能只在受邀之後才看得到),包括其他使用者登入的裝置。Remote sessions can be created by any Windows device, and any Windows device can request to join (although sessions can have invite-only visibility), including devices signed in by other users. 本指南針對所有會使用遠端工作階段的主要案例來提供基本範例程式碼。This guide provides basic sample code for all of the major scenarios that make use of remote sessions. 您可以將此程式碼納入現有的應用程式專案,並視需要進行修改。This code can be incorporated into an existing app project and modified as necessary. 如需端對端實作,請參閱測驗遊戲範例應用程式)。For an end-to-end implementation, see the Quiz Game sample app).

初步設定Preliminary setup

新增 remoteSystem 功能Add the remoteSystem capability

為了讓您的應用程式能夠啟動遠端裝置上的應用程式,您必須將 remoteSystem 功能新增至應用程式套件資訊清單。In order for your app to launch an app on a remote device, you must add the remoteSystem capability to your app package manifest. 您可以使用套件資訊清單設計工具,在 [功能] 索引標籤上選取 [遠端系統] 來新增它,也可以手動將下列這一行加入至專案的_package.appxmanifest_檔案。You can use the package manifest designer to add it by selecting Remote System on the Capabilities tab, or you can manually add the following line to your project's Package.appxmanifest file.

<Capabilities>
   <uap3:Capability Name="remoteSystem"/>
</Capabilities>

在裝置上啟用跨使用者探索Enable cross-user discovering on the device

遠端工作階段是專為連接多個不同的使用者而設計,因此相關的裝置都必須啟用 [跨使用者共用]。Remote Sessions is geared toward connecting multiple different users, so the devices involved will need to have Cross-User Sharing enabled. 這是可以使用 RemoteSystem 類別的靜態方法查詢的系統設定:This is a system setting that can be queried with a static method in the RemoteSystem class:

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".
}

若要變更此設定,使用者必須開啟 [設定] App。To change this setting, the user must open the Settings app. 在 [系統 > 共用體驗 > 跨裝置共用] 功能表中,有一個下拉式方塊可讓使用者指定其系統可共用的裝置。In the System > Shared experiences > Share across devices menu, there is a drop-down box where the user can specify which devices their system can share with.

共用體驗設定頁面

包含必要的命名空間Include the necessary namespaces

為了使用本指南中所有的程式碼片段,您的類別檔案需要下列 using 陳述式。In order to use all of the code snippets in this guide, you will need the following using statements in your class file(s).

using System.Runtime.Serialization.Json;
using Windows.Foundation.Collections;
using Windows.System.RemoteSystems;

建立遠端工作階段Create a remote session

若要建立遠端工作階段執行個體,您必須從 RemoteSystemSessionController 物件開始著手。To create a remote session instance, you must start with a RemoteSystemSessionController object. 使用下列架構來建立新的工作階段,以及處理其他裝置的加入要求。Use the following framework to create a new session and handle join requests from other devices.

public async void CreateSession() {
    
    // create a session controller
    RemoteSystemSessionController manager = new RemoteSystemSessionController("Bob’s Minecraft game");
    
    // register the following code to handle the JoinRequested event
    manager.JoinRequested += async (sender, args) => {
        // Get the deferral
        var deferral = args.GetDeferral();
        
        // display the participant (args.JoinRequest.Participant) on UI, giving the 
        // user an opportunity to respond
        // ...
        
        // If the user chooses "accept", accept this remote system as a participant
        args.JoinRequest.Accept();
    };
    
    // create and start the session
    RemoteSystemSessionCreationResult createResult = await manager.CreateSessionAsync();
    
    // handle the creation result
    if (createResult.Status == RemoteSystemSessionCreationStatus.Success) {
        // creation was successful, get a reference to the session
        RemoteSystemSession currentSession = createResult.Session;
        
        // optionally subscribe to the disconnection event
        currentSession.Disconnected += async (sender, args) => {
            // update the UI, using args.Reason
            //...
        };
    
        // Use session (see later section)
        //...
    
    } else if (createResult.Status == RemoteSystemSessionCreationStatus.SessionLimitsExceeded) {
        // creation failed. Optionally update UI to indicate that there are too many sessions in progress
    } else {
        // creation failed for an unknown reason. Optionally update UI
    }
}

將遠端工作階段設定為僅限透過邀請加入Make a remote session invite-only

如果您想要讓遠端工作階段無法公開供探索,您可以將其設定為僅限透過邀請加入。If you wish to keep your remote session from being publicly discoverable, you can make it invite-only. 只有收到邀請的裝置才可以傳送加入要求。Only the devices that receive an invitation will be able to send join requests.

程序大多與上述相同,但建構 RemoteSystemSessionController 執行個體時,您要傳入已設定的 RemoteSystemSessionOptions 物件。The procedure is mostly the same as above, but when constructing the RemoteSystemSessionController instance, you will pass in a configured RemoteSystemSessionOptions object.

// define the session options with the invite-only designation
RemoteSystemSessionOptions sessionOptions = new RemoteSystemSessionOptions();
sessionOptions.IsInviteOnly = true;

// create the session controller
RemoteSystemSessionController manager = new RemoteSystemSessionController("Bob's Minecraft game", sessionOptions);

//...

若要傳送邀請,您必須有接收端遠端系統的參考 (透過一般遠端系統探索取得)。To send an invitation, you must have a reference to the receiving remote system (acquired through normal remote system discovery). 只需將此參考傳入工作階段物件的 SendInvitationAsync 方法即可。Simply pass this reference into the session object's SendInvitationAsync method. 工作階段中所有的參與者都有遠端工作階段的參考 (請參閱下一節),因此任何參與者皆可傳送邀請。All of the participants in a session have a reference to the remote session (see next section), so any participant can send an invitation.

// "currentSession" is a reference to a RemoteSystemSession.
// "guestSystem" is a previously discovered RemoteSystem instance
currentSession.SendInvitationAsync(guestSystem); 

探索並加入遠端工作階段Discover and join a remote session

探索遠端工作階段的程序是由 RemoteSystemSessionWatcher 類別處理,類似於探索個人遠端系統。The process of discovering remote sessions is handled by the RemoteSystemSessionWatcher class and is similar to discovering individual remote systems.

public void DiscoverSessions() {
    
    // create a watcher for remote system sessions
    RemoteSystemSessionWatcher sessionWatcher = RemoteSystemSession.CreateWatcher();
    
    // register a handler for the "added" event
    sessionWatcher.Added += async (sender, args) => {
        
        // get a reference to the info about the discovered session
        RemoteSystemSessionInfo sessionInfo = args.SessionInfo;
        
        // Optionally update the UI with the sessionInfo.DisplayName and 
        // sessionInfo.ControllerDisplayName strings. 
        // Save a reference to this RemoteSystemSessionInfo to use when the
        // user selects this session from the UI
        //...
    };
    
    // Begin watching
    sessionWatcher.Start();
}

取得 RemoteSystemSessionInfo 執行個體時,可以用來向控制對應工作階段的裝置發出加入要求裝置。When a RemoteSystemSessionInfo instance is obtained, it can be used to issue a join request to the device that controls the corresponding session. 接受的加入要求將以非同步方式傳回包含所加入工作階段之參考的 RemoteSystemSessionJoinResult 物件。An accepted join request will asynchronously return a RemoteSystemSessionJoinResult object that contains a reference to the joined session.

public async void JoinSession(RemoteSystemSessionInfo sessionInfo) {

    // issue a join request and wait for result.
    RemoteSystemSessionJoinResult joinResult = await sessionInfo.JoinAsync();
    if (joinResult.Status == RemoteSystemSessionJoinStatus.Success) {
        // Join request was approved

        // RemoteSystemSession instance "currentSession" was declared at class level.
        // Assign the value obtained from the join result.
        currentSession = joinResult.Session;
        
        // note connection and register to handle disconnection event
        bool isConnected = true;
        currentSession.Disconnected += async (sender, args) => {
            isConnected = false;

            // update the UI with args.Reason value
        };
        
        if (isConnected) {
            // optionally use the session here (see next section)
            //...
        }
    } else {
        // Join was unsuccessful.
        // Update the UI, using joinResult.Status value to show cause of failure.
    }
}

可以同時將裝置加入多個工作階段。A device can be joined to multiple sessions at the same time. 因此,最好是將加入功能從實際與每個工作階段的互動脫離。For this reason, it may be desirable to separate the joining functionality from the actual interaction with each session. 只要 RemoteSystemSession 執行個體的參考保留在應用程式中,就可以透過該工作階段嘗試進行通訊。As long as a reference to the RemoteSystemSession instance is maintained in the app, communication can be attempted over that session.

透過遠端工作階段分享訊息和資料Share messages and data through a remote session

接收訊息Receive messages

您可以使用 RemoteSystemSessionMessageChannel 執行個體 (代表單一全工作階段範圍的通訊管道) 來和工作階段中的其他參與者裝置交換訊息及資料。You can exchange messages and data with other participant devices in the session by using a RemoteSystemSessionMessageChannel instance, which represents a single session-wide communication channel. 只要已初始化,它就會開始接聽訊息。As soon as it's initialized, it begins listening for incoming messages.

注意

傳送和接收訊息時,必須將訊息從位元組陣列序列化並還原序列化。Messages must be serialized and deserialized from byte arrays upon sending and receiving. 這項功能已包含下列範例中,但為了達到更好的程式碼模組化,您可以個別實作該功能。This functionality is included in the following examples, but it can be implemented separately for better code modularity. 如需這項功能的範例,請參閱範例應用程式)。See the sample app) for an example of this.

public async void StartReceivingMessages() {
    
    // Initialize. The channel name must be known by all participant devices 
    // that will communicate over it.
    RemoteSystemSessionMessageChannel messageChannel = new RemoteSystemSessionMessageChannel(currentSession, 
        "Everyone in Bob's Minecraft game", 
        RemoteSystemSessionMessageChannelReliability.Reliable);
    
    // write the handler for incoming messages on this channel
    messageChannel.ValueSetReceived += async (sender, args) => {
        
        // Update UI: a message was received from the participant args.Sender
        
        // Deserialize the message 
        // (this app must know what key to use and what object type the value is expected to be)
        ValueSet receivedMessage = args.Message;
        object rawData = receivedMessage["appKey"]);
        object value = new ExpectedType(); // this must be whatever type is expected

        using (var stream = new MemoryStream((byte[])rawData)) {
            value = new DataContractJsonSerializer(value.GetType()).ReadObject(stream);
        }
        
        // do something with the "value" object
        //...
    };
}

傳送訊息Send messages

建立管道後,將訊息傳送給所有的工作階段參與者就很簡單。When the channel is established, sending a message to all of the session participants is straightforward.

public async void SendMessageToAllParticipantsAsync(RemoteSystemSessionMessageChannel messageChannel, object value){

    // define a ValueSet message to send
    ValueSet message = new ValueSet();
    
    // serialize the "value" object to send
    using (var stream = new MemoryStream()){
        new DataContractJsonSerializer(value.GetType()).WriteObject(stream, value);
        byte[] rawData = stream.ToArray();
            message["appKey"] = rawData;
    }
    
    // Send message to all participants. Ordering is not guaranteed.
    await messageChannel.BroadcastValueSetAsync(message);
}

若要傳送訊息只限給特定參與者,您必須先起始探索程序,才能取得工作階段中遠端系統參與者的參考。In order to send a message to only certain participant(s), you must first initiate a discovery process to acquire references to the remote systems participating in the session. 這與探索工作階段以外遠端系統的程序相似。This is similar to the process of discovering remote systems outside of a session. 使用 RemoteSystemSessionParticipantWatcher 執行個體來尋找工作階段的參與者裝置。Use a RemoteSystemSessionParticipantWatcher instance to find a session's participant devices.

public void WatchForParticipants() {
    // "currentSession" is a reference to a RemoteSystemSession.
    RemoteSystemSessionParticipantWatcher watcher = currentSession.CreateParticipantWatcher();

    watcher.Added += (sender, participant) => {
        // save a reference to "participant"
        // optionally update UI
    };   

    watcher.Removed += (sender, participant) => {
        // remove reference to "participant"
        // optionally update UI
    };

    watcher.EnumerationCompleted += (sender, args) => {
        // Apps can delay data model render up until this point if they wish.
    };

    // Begin watching for session participants
    watcher.Start();
}

取得工作階段參與者的參考清單時,您可以傳送訊息給任何一組參與者。When a list of references to session participants is obtained you can send a message to any set of them.

若要傳送訊息給單一參與者 (最好由使用者在畫面上選取),只需將參考傳入方法中,如下所示。To send a message to a single participant (ideally selected on-screen by the user), simply pass the reference into a method like the following.

public async void SendMessageToParticipantAsync(RemoteSystemSessionMessageChannel messageChannel, RemoteSystemSessionParticipant participant, object value) {
    
    // define a ValueSet message to send
    ValueSet message = new ValueSet();
    
    // serialize the "value" object to send
    using (var stream = new MemoryStream()){
        new DataContractJsonSerializer(value.GetType()).WriteObject(stream, value);
        byte[] rawData = stream.ToArray();
            message["appKey"] = rawData;
    }

    // Send message to the participant
    await messageChannel.SendValueSetAsync(message,participant);
}

若要傳送訊息給多個參與者 (最好由使用者在畫面上選取),請將這些參與者新增至清單物件,然後傳入方法中,如下所示。To send a message to multiple participants (ideally selected on-screen by the user), add them to a list object and pass the list into a method like the following.

public async void SendMessageToListAsync(RemoteSystemSessionMessageChannel messageChannel, IReadOnlyList<RemoteSystemSessionParticipant> myTeam, object value){

    // define a ValueSet message to send
    ValueSet message = new ValueSet();
    
    // serialize the "value" object to send
    using (var stream = new MemoryStream()){
        new DataContractJsonSerializer(value.GetType()).WriteObject(stream, value);
        byte[] rawData = stream.ToArray();
            message["appKey"] = rawData;
    }

    // Send message to specific participants. Ordering is not guaranteed.
    await messageChannel.SendValueSetToParticipantsAsync(message, myTeam);   
}