Delen via


OC 대화창 옆에 TAB 붙이는 방법

Context Windows in Communicator Using UCMA v2.0

Office Communicator has a number of useful but mostly unknown features. One of these is its ability to display a custom context pane in a conversation window. The context pane shows up where the video goes in a video chat, and displays HTML content from any intranet or trusted URL.Conversation window with context pane

The way to trigger this window is to send a SIP INFO message with a particular type of XML document to the conversation. All of the remote participants that receive the message will then get the context pane in Communicator if they are configured appropriately.

Notice the catch: there are a couple of things that need to be configured on each client in order for the context pane to show up.

First, a registry setting needs to be set to enable the context pane functionality in Communicator. To do this, open up regedit.exe and navigate to:

 HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Communicator

You may have to create some of those folders if they don’t already exist. Just right-click on the level above it and select New Key.

Once you’ve found the right key, create a DWORD (32-bit) value called EnableConversationWindowTabs, with a value of 1.

Regedit with EnableConversationWindowTabs setting

So much for registry settings. Next, if the URL you want to use for the content of your context window is not on your local intranet, you will need to add it to your trusted sites. You can do this through Internet Options in Internet Explorer.

Internet Options Security tab in Internet ExplorerTrusted Sites in Internet Explorer

Once everything is configured properly, you’ll be able to see the context window when your OCS endpoint receives a SIP INFO message with “call associated data” information.

The code to send this message through UCMA v2.0 is actually quite simple. You will need the format of the XML document, as well as the content type; I like to keep these in constants so the code stays clean.

Here is the text of the XML document. (The {0} in the XML block represents a unique ID for each context pane, and the {1} represents the URL of the content.)

 private const string CONTEXT_WINDOW_MESSAGE = "<associatedData xmlns=\"https://" + <br>            "schemas.microsoft.com/LCS/2007/04/CallAssociatedData\" xmlns:xsi=\"https://" + <br>            "www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"https://" + <br>            "schemas.microsoft.com/LCS/2007/04/CallAssociatedData\">" + <br>            "<associatedDataElement id=\"{0}\" purpose=\"CWurl\">" + <br>            "<urlToHost>{1}</urlToHost><closeWithWindow>false</closeWithWindow>" + <br>            "</associatedDataElement></associatedData>";

Here is the content type:

 private const string CONTEXT_WINDOW_CONTENT_TYPE = "application/ms-call-associated-data+xml";

An integer instance variable will help keep track of the next unique ID in the sequence.

 private int _contextWindowId = 1;

Before you can send the INFO message, you will already have had to set up a CollaborationPlatform, endpoint, conversation, and call. If you need help with the steps, you should be able to find detailed instructions in the UCMA v2.0 SDK documentation, or you can look at some of the samples that come packaged with the SDK.

In order to send the INFO message, we need to call the BeginSendInfo method on an established call. The BeginSendInfo method takes a byte array for the content of the INFO message, so the next step is to encode the XML document as a byte array:

  byte[] bytes = Encoding.UTF8.GetBytes(string.Format(CONTEXT_WINDOW_MESSAGE, <br>                _contextWindowId++, _url));

Notice that I’m plugging the context window ID and the content URL into the XML document, and also incrementing the context window ID.

Finally, we call BeginSendInfo on the Call object, passing in the content type, the byte array, and a callback delegate. If everything is set up correctly, the remote participant(s) in the call will see the context pane appear in their conversation windows.

 infoMessageCall.BeginSendInfo(<br>                new System.Net.Mime.ContentType(CONTEXT_WINDOW_CONTENT_TYPE), bytes, null, <br>                asyncResult => infoMessageCall.EndSendInfo(asyncResult), null<br>            );

If a configuration problem is preventing the context window from working properly, you should see an error message in the conversation window that says “Could not display a form related to this conversation, which may prevent access to some options. If this problem persists, contact your system administrator.” If you are getting this message, check the registry setting again, and make sure that the URL you are using is a trusted site. You can also try looking at the properties of the SipResponseData object that is returned by the EndSendInfo method; this may give you a clue to the problem.

Note that you are perfectly free to pass in a query string parameter in the URL you send. You can do some really cool contextual things by linking to server-side applications that display data dynamically based on the parameters passed in with the URL.

Here is the complete code of my ContextWindowMessage class:

 /// Handles the sending of the INFO message that causes the extended window 
 /// to appear in Communicator.
 /// </summary>
 public class ContextWindowMessage {
  
     #region Constants
  
     private const string CONTEXT_WINDOW_MESSAGE = "<associatedData xmlns=\"https://" +
         "schemas.microsoft.com/LCS/2007/04/CallAssociatedData\" xmlns:xsi=\"https://" +
         "www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"https://" +
         "schemas.microsoft.com/LCS/2007/04/CallAssociatedData\">" +
         "<associatedDataElement id=\"{0}\" purpose=\"CWurl\">" +
         "<urlToHost>{1}</urlToHost><closeWithWindow>false</closeWithWindow>" +
         "</associatedDataElement></associatedData>";
     private const string CONTEXT_WINDOW_CONTENT_TYPE = 
         "application/ms-call-associated-data+xml";
  
     #endregion
  
     private int _contextWindowId = 1;
     private string _url = string.Empty;
  
     public ContextWindowMessage(string url) {
         if (string.IsNullOrEmpty(url)) {
             throw new ArgumentException("URL cannot be null.");
         }
  
         _url = url;
     }
  
     /// <summary>
     /// Sends the INFO message with the URL for the extended window.
     /// </summary>
     /// <param name="infoMessageCall">The call in which to send the INFO message.</param>
     public void SendToCall(Call infoMessageCall) {
  
         // We need to supply the INFO message as a byte array. The message itself is an 
         // XML document, whose format is in the CONTEXT_WINDOW_MESSAGE constant.
  
         byte[] bytes = Encoding.UTF8.GetBytes(string.Format(CONTEXT_WINDOW_MESSAGE, 
             _contextWindowId++, _url));
  
         // To send the INFO message, we use the BeginSendInfo method on the Call 
         // (not the Flow). In order for the extended window to appear, the first
         // parameter (the content type) should be "application/ms-call-associated-data+xml".
  
         infoMessageCall.BeginSendInfo(
             new System.Net.Mime.ContentType(CONTEXT_WINDOW_CONTENT_TYPE), bytes, null, 
             asyncResult => infoMessageCall.EndSendInfo(asyncResult), null
         );
     }
 }