Lync の Contextual Conversation 開発

環境 : Lync Server 2010, Lync 2010, Visual Studio 2010, Silverlight 4 開発者向けランタイム, Lync 2010 SDK

Lync 2010 クライアント開発入門

こんにちは。

今回は、これまで説明した開発手法を使って、Lync クライアント開発の「本丸」とも言うべき、Contextual Conversation を活用した開発について説明します。

Contextual Conversation とは ?

Lync SDK を使った開発 (Development) では、わざわざ Lync クライアントと同じ機能を作っても楽しくありません。開発者の皆さんが作成するカスタム アプリケーションが持っている機能を活かしながら、そのアプリケーションの機能の一部として自然な形で Lync クライアントの機能を組み合わせていくと良いでしょう。

例えば、ある業務アプリケーションで、そのアプリケーションの機能の一部のように仕事仲間とコミュニケーションをおこなって協調作業できるアプリケーションが考えられます。アプリケーションの中で議論すべき内容がある場合、そのアプリケーション上の内容をお互いに共有しながら、Lync の Conversation 機能などが使えると実用的でしょう。また、Lync (または Office Communicator) と Outlook を同時に使われたことがある方は、Outlook に IM (Instant Messaging) 連携のための機能が付いているのをご存じでしょう。あるメールに関してリアルタイムに相手とコミュニケーションをおこないたい場合に使うことができますが、この機能でも、ここで説明する Contextual Conversation が使用されています。

こうした連携でポイントとなるのは、「今、アプリケーションの中のどの内容についてコミュニケーションをおこなっているのか」という関係付けです。もし、カスタム アプリケーションと Lync の間でこうした関係がいっさい無いならば、Lync クライアントをアプリケーションとは別に起動すれば良いわけです。このため、こうした連携においては、相互の関係性がポイントになってきます。これが 「コンテキスト」(Context) になります。

このコンテキスト (Context) によるカスタム アプリケーションと Lync の連携には、以下の 2 通りの連携方法があります。

  • Simple Context
    ハイパーリンク (URL) を使った簡易な連携方法です。特別な準備も必要なく、非常にわかりやすい連携方法です。
  • Rich Context
    この方法では、後述するように、さらに高度な連携が可能です。ただし、Simple Context と異なり、アプリケーション側にいくつかの準備が必要になります。

補足 : このポストでは、Rich Context による Contextual Conversation を、CWE を中心に説明しますが、ここで説明する Rich Context は、通常のデスクトップ アプリケーションなどでも使用できます。また、Instant Messaging (IM) 以外の Conversation (Audio Conversation など) でも使用できます。

補足 : ここでは、StartInstantMessagingButton コントロールを使いますが、Conversation の開始をプログラム コードを使って記述することもできます。(AddConversation メソッドを使用します。)
参考として、このサンプル コードを最後 (下記) に掲載しておきます。

ハイパーリンクを使った呼び出し (Simple Context)

まずは、ハイパーリンクを使った Simple Context について説明しましょう。

動作は非常に簡単です。カスタム アプリケーションから、ある内容について Conversation をおこないたい場合に、Conversation Windows を起動し、この Conversation Window の中に 議論したい内容へのリンク (URL) を埋め込むというものです。
さっそくサンプル コードを作成してみましょう。

今回は、コードを複雑にしないために、StartInstantMessagingButton コントロールだけを持った簡単なカスタム アプリケーションを例に説明します。

前回までと同様に、Visual Studio を起動して Silverlight アプリケーションを作成し、Microsoft.Lync.Controls.dll、Microsoft.Lync.Controls.Framework.dll、Microsoft.Lync.Model.dll、Microsoft.Lync.Utilities.dll の各アセンブリを参照追加しておきます。
MainPage.xaml に StartInstantMessagingButton コントロールを追加しましょう。(このコントロールをクリックすると、Conversation Window が表示され、Conversation が開始されます。)

第 2 回 と同様、挿入した StartInstantMessagingButton コントロールの Source プロパティに、対話をおこなう相手の SIP のアドレスを設定します。(なお、現実のアプリケーションでは、アプリケーションの状態に応じて、動的に Source プロパティを変更すると良いでしょう。)

つぎに、MainPage.xaml をダブルクリックして、以下の通りコードを記述します。(ここがポイントです !)
今回は、このブログについての議論をおこなうという想定で、上記の StartInstantMessagingButton コントロール (startInstantMessagingButton1) をクリックすると、「http://blogs.msdn.com/b/tsmatsuz」のリンク情報を含んだ Lync クライアントを起動するように、コンテキストを設定しています。

. . .
using Microsoft.Lync.Controls;

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    ConversationContextualInfo context = new ConversationContextualInfo();
    context.Subject = "会話のテスト";
    context.ContextualLink = @"http://blogs.msdn.com/b/tsmatsuz";
    startInstantMessagingButton1.ContextualInformation = context;
}

以上で完了です。このアプリケーションを動作させてみましょう。

上記の Silverlight アプリケーションを実行して StartInstantMessagingButton をクリックすると、Conversation Window が起動します。この Conversation Window にテキストを入力して対話を開始すると、送信側 / 受信側の双方に下図のようにリンクが表示されます。

上記の通り、このハイパーリンクを使った連携は、特別な事前準備などは、いっさい不要です。リンク先 (上記の URL) も、信頼済みサイト (Trusted Site) である必要はありません。

しかし、この連携では、単純なハイパーリンクによるアプリケーション (コンテキスト) の起動のみがサポートされます。例えば、付帯データ (起動パラメーターなど) を渡す場合には、URL のクエリ文字列などの形で、URL の一部として渡す必要があります。また、このあと説明するリアルタイムなコンテキスト データ送信など、高度な連携もおこなうことはできません。

なお、上記を、下記の通り XAML だけで記述しても構いませんが、デザイン タイムのサポートはないので、XAML をテキストで記入してください。

<Grid x:Name="LayoutRoot" Background="White">
  <Grid.Resources>
    <controls:ConversationContextualInfo
      x:Key="simpleLinkContext"
      ContextualLink="http://blogs.msdn.com/b/tsmatsuz" />
  </Grid.Resources>
  <controls:StartInstantMessagingButton Name="startInstantMessagingButton1"
    Source="demouser2@example.jp"
    ContextualInformation="{StaticResource simpleLinkContext}" />
</Grid>

Contextual Conversation による CWE の呼び出し (Rich Context)

では、ここからは、Rich Context について説明し、合わせて、いくつかの高度な機能も解説していきましょう。

ここでは、前回、レジストリーに登録した Conversation Window Extension (CWE) を使用し、Contextual Conversation の動作を確認します。先ほどと同様、StartInstantMessagingButton コントロールをクリックすると Conversation Window が起動しますが、今回は、CWE の中に、アプリケーションに関係する情報 (コンテキスト) を表示します。

前回、CWE の登録の際に、HKEY_CURRENT_USER にレジストリー登録していたことを思い出してください。このため、事前に、Conversation Window でコミュニケーションをおこなう双方のユーザーのレジストリー エントリーに、前回 の設定をおこなっておきましょう。(この設定を利用時に促すことも可能ですが、この方法については後述します。まずは、手動で、それぞれのレジストリー設定をおこなっておいてください。)

今度は、上記のコードの代わりに、MainPage.xaml.cs (上記のアプリケーション) に下記の通り記述します。

. . .
using Microsoft.Lync.Controls;

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    ConversationContextualInfo context = new ConversationContextualInfo();
    context.Subject = "会話のテスト";
    context.ApplicationId = "{95CF283C-708C-4D3C-BADF-06456ED07D60}";
    startInstantMessagingButton1.ContextualInformation = context;
}

アプリケーション Id として、前回 レジストリーに登録したアプリケーションの Guid (95CF283C-708C-4D3C-BADF-06456ED07D60) の文字列を指定していることに注意してください。この設定だけで、Conversation Window に、この Guid の CWE が表示されるようになります。

動作確認は、この後 おこないますので、先に進めましょう。

起動パラメーターの設定

つぎに、上記の Silverlight アプリケーションから、Conversation Window に、アプリケーション データ (パラメーター) を渡してみましょう。
現実の開発では、アプリケーションで選択中のアイテムの情報などを取得し、この情報を CWE の上で表示すると良いでしょう。ここでは、単に、「テスト用のデータ」という文字列を Conversation Window に渡して、Conversation Window Extension (CWE) の上に この文字列を表示するという単純なアプリケーションを作成してみます。

下記太字の通り追記します。

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    ConversationContextualInfo context = new ConversationContextualInfo();
    context.Subject = "会話のテスト";
    context.ApplicationId = "{95CF283C-708C-4D3C-BADF-06456ED07D60}";
    context.ApplicationData = "テスト用のデータ";
    startInstantMessagingButton1.ContextualInformation = context;
}

つぎに、データを受け取る CWE 側の処理を作成します。
前回作成した Conversation Window Extension のアプリケーションで、ロード時の処理として、以下のコードを追加します。(今回は、単純に、TextBlock に受信したデータを表示します。)

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    // Conversation を取得
    con = (Conversation)LyncClient.GetHostingConversation();

    . . .

    // コンテキスト受信時のアプリケーション データを取得して表示
    textBlock1.Text = con.GetApplicationData("{95CF283C-708C-4D3C-BADF-06456ED07D60}");
}

これで、データの送信側と受信側の双方の処理が完了しました。では、動作を確認してみましょう。

上記の Silverlight アプリケーションを実行して StartInstantMessagingButton をクリックすると、Conversation Window が起動し、このConversation Window には、右ペインに CWE が表示されます。
この CWE には、送信側 / 受信側の双方で、 下図の通り、渡されたアプリケーション データが表示されます。

補足 : 前回 (第 4 回で)、CWE を「Context Packages」とも表現していましたが、CWE は、こうした Context 連携機能の 1 つにすぎません。
例えば、レジストリーに、「InternalURL」、「ExternalURL」 の代わりに、「Path」の値 (インストールされているアプリケーションのフルパス) を指定しておくと、Contextual Conversation をおこなった場合に、CWE の変わりに、下図の通りリンクが表示され、このリンクをクリックするとデスクトップ アプリケーション (exe) が起動して連携をおこなうことができます。 (この場合、exe の入力パラメータとして、アプリケーション データを受け取ることができます。)

カスタム アプリケーションを初めて使用する際などに、こうした Lync 用の Plug-in の登録 (レジストリー登録) をユーザーごとにおこなっておくと、Lync を使って、インストールされているデスクトップ アプリケーション間でデータを共有したコミュニケーションが可能です。

インストール用のリンク (InstallLink) の提供

上記の Rich Context によるアプリケーション連携では、受信側にもアプリケーション登録 (レジストリー登録) が必要であるという点に注意してください。(なお、前回説明した Run-Time Registration を使えば、レジストリー登録は必要ありませんが、受信側でも同様に Run-Time Registration が必要になるという点は変わりません。)
もし、受信側でアプリケーションが登録されていない場合、受信側では以下のようなエラー画面が表示されてしまいます。

こうした場合の優れた解決策として、レジストリー登録 (Install Registration) のためのインストール用のリンクを提供することができます。

下図のように、送信側のレジストリーに InstallLink という名前のエントリを作成して、ここに、プラグインをインストールするモジュールの URL を指定しておきます。(この URL にアクセスすることで、レジストリー登録をおこなうようにします。)

すると、受信側では、下記の通りプラグインのインストール用リンクが表示されます。受信側は、このリンクをクリックしてインストールをおこない、そのあとで、その下の起動用のリンクをクリックすると、CWE が表示されるようになります。

現実のアプリケーションの場合には、アプリケーション内にこうしたインストール用のリンクを設けてインストール作業 (レジストリー登録) を利用者に促すと良いでしょう。また、インストール時は、レジストリー設定によって、インストールと同時に「信頼済みサイト」(Trusted Site) の設定などもおこなっておくと良いでしょう。(こうすることで、利用者に対し、必要なすべての設定作業を自動化することができます。)

補足 : 例えば、Internet Explorer の信頼済みサイトに http://myserver のサイトを登録するには、下記の通りレジストリーに設定します。(下記は、.reg ファイルを使用した場合のサンプルです。)

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionInternet SettingsZoneMapDomainsmyserver]
"http"=dword:00000002

継続的なコンテキスト データの送信

上記のアプリケーション データ (ApplicationData) ではアプリケーション開始時の起動パラメーターのみを渡していましたが、Rich Context では、さらに継続的なアプリケーションどうしの連携シナリオを扱うことができます。

例えば、下記のように、送信側でボタンを押すたびに現在時刻を送信し、受信側では、受け取ったデータ (現在時刻) を表示する CWE のアプリケーションを作成してみましょう。(ただし、後述のコードのように、送信時は、初回の送信と 2 回目以降の送信で呼び出すメソッドが異なるため、今回は、下図の通り、ボタンを 2 つ配置しておきます。)

この連携アプリケーションを構築するには、CWE の Silverlight アプリケーションに 2 つのボタン (button1, button2) と TextBlock (textBlock1) を配置して、以下の通りコードを記述すれば OK です。

. . .
using Microsoft.Lync.Model;
using Microsoft.Lync.Model.Extensibility;
using Microsoft.Lync.Model.Conversation;

. . .

private Conversation con;   // conversation

public MainPage()
{
    InitializeComponent();
}

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    // Conversation を取得
    con = (Conversation)LyncClient.GetHostingConversation();

    // コンテキスト受信時のアプリケーションデータの取得
    con.InitialContextReceived += new EventHandler<InitialContextEventArgs>(con_InitialContextReceived);
    con.ContextDataReceived += new EventHandler<ContextEventArgs>(con_ContextDataReceived);
}

// 最初のコンテキストデータの受信時
void con_InitialContextReceived(object sender, InitialContextEventArgs e)
{
    textBlock1.Text = e.ApplicationData;
}

// 2 回目以降のコンテキストデータの受信時
void con_ContextDataReceived(object sender, ContextEventArgs e)
{
    textBlock1.Text = e.ContextData;
}

// 最初のコンテキストデータの送信ボタン
private void button1_Click(object sender, RoutedEventArgs e)
{
    Dictionary<ContextType, object> context = new Dictionary<ContextType, object>();
    context.Add(ContextType.ApplicationId, "{95CF283C-708C-4D3C-BADF-06456ED07D60}");
    context.Add(ContextType.ApplicationData, DateTime.Now.ToLongTimeString());
    con.BeginSendInitialContext(context, SendContextDataCallback, null);
}

// 2 回目以降のコンテキストデータの送信ボタン
private void button2_Click(object sender, RoutedEventArgs e)
{
    con.BeginSendContextData("{95CF283C-708C-4D3C-BADF-06456ED07D60}", @"text/plain", DateTime.Now.ToLongTimeString(), SendContextDataCallback, null);
}

// 送信時のコールバック関数
public void SendContextDataCallback(IAsyncResult res)
{
    con.EndSendContextData(res);
}

このアプリケーションでは、最初に button1 を押し、2 回目以降に button2 を押すと、毎回、受信側に現在時刻が表示 (更新) されるようになります。(この手法は、デスクトップ アプリケーションどうしでもおこなうことができます。)

上記は、単なる画面共有 (デスクトップの共有) ではなく、アプリケーションそのものを共有しています。この手法を使って、カスタム アプリケーション独自なリアルタイム連携機能を実装することができます。(なお、単に画面操作を共有したいだけならば、Lync 2010 には、そもそも、Conversation の最中に、起動中のデスクトップ (画面) やプログラム (アプリケーション) を共有する機能が付いています。プログラム共有では、相互に制御を渡して操作することが可能です。)

さいごに

このように、Contextual Conversation を使うと、Lync で使用している Conversation のためのプロトコルを使用して、作業者どうしで相互に協調作業をおこう高度なアプリケーションも構築できます。Lync は、もはや、単なるコミュニケーション ツールではなく、リアルタイム連携のためのアプリケーション開発基盤そのものであることがお分かり頂けるでしょう。
また、Lync Online を使用すると、 こうしたアプリケーション連携をインターネット越しに実現できることになります。Windows Azure などに CWE アプリケーションやインストール用のモジュールなどを配置しておくことで、”Internet Ready” なリアルタイム連携のアプリケーションを構築できます。

[参考] プログラミング (Programming) による Contextual Conversation の開始

上記では StartInstantMessagingButton コントロールを使用して Contextual Conversation を開始しましたが、プログラム コードを使って開始することもできます。
以下に、ご参考のため、Rich Context を開始するサンプル コードを記載しておきます。

. . .

private LyncClient cl;

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    . . .

    cl = LyncClient.GetClient();
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    cl.ConversationManager.ConversationAdded += new EventHandler<ConversationManagerEventArgs>(ConversationAddedHandler);
    Conversation con = cl.ConversationManager.AddConversation();
}

private void ConversationAddedHandler(object sender, ConversationManagerEventArgs e)
{
    cl.ConversationManager.ConversationAdded -= new EventHandler<ConversationManagerEventArgs>(ConversationAddedHandler);

    Dictionary<ContextType, object> ctxInfo = new Dictionary<ContextType, object>();
    ctxInfo.Add(ContextType.ApplicationId, "{95CF283C-708C-4D3C-BADF-06456ED07D60}");
    ctxInfo.Add(ContextType.ApplicationData, "テスト用のデータ");
    e.Conversation.BeginSendInitialContext(ctxInfo, BeginSendInitialContextCallback, e.Conversation);

    e.Conversation.ParticipantAdded += new EventHandler<ParticipantCollectionChangedEventArgs>(ParticipantAddedHandler);
    e.Conversation.AddParticipant(LyncClient.GetClient().ContactManager.GetContactByUri("demouser2@example.jp"));
}

private void ParticipantAddedHandler(object sender, ParticipantCollectionChangedEventArgs e)
{
    Conversation con = (Conversation)sender;

    // 本人と相手の 2 回到達するため、CanInvoke で確認
    if (con.Modalities[ModalityTypes.InstantMessage].CanInvoke(ModalityAction.Connect))
    {
        con.Modalities[ModalityTypes.InstantMessage].BeginConnect(
            BeginConnectCallback,
            con.Modalities[ModalityTypes.InstantMessage]);
    }
}

private void BeginSendInitialContextCallback(IAsyncResult res)
{
    if (res.IsCompleted == true)
        ((Conversation)res.AsyncState).EndSendInitialContext(res);
}

private void BeginConnectCallback(IAsyncResult res)
{
    if (res.IsCompleted == true)
        ((InstantMessageModality)res.AsyncState).EndConnect(res);
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s