はじめに
前回に引き続いて、PowerPointアドイン開発の後篇です。スライドショーの最中に最新の写真をSkyDriveからダウンロードしてスライドに表示するアドインを作ります(図1)。
Officeアドインで非同期処理
前回でほとんどの準備を行いましたが、メインの処理を書く前に、準備をもうひとつ行います。Officeのアドインでスレッドを使った非同期処理を行う場合、一般的な決まった方法があり、今回はその内容を実装します。
ネットワークアクセスなど時間のかかる処理は、バックグランドで行い、完了時に結果をOfficeアプリ(今回の場合はPowerPoint)に反映したい場合があります。この結果を反映しようとしたとき、Officeアプリがすぐに処理を実行できない可能性があります。その場合、例外が発生するので、アドイン側で再試行などの処理をする方法、または、IMessageFilterインターフェイスを実装する方法があります。今回はIMessageFilterインターフェイスを実装する方法を採ります。詳しくは、Office でのスレッドのサポートなどを参照してください。
IMessageFilterの実装
まず、インターフェイスを用意します。Visual Studioのメニュー[プロジェクト]の[新しい項目の追加]から、インターフェイスを選択・追加します。IMessageFilter.vbというファイル名にします。そして、次のようにコードを編集してください。
このインターフェイスをThisAddInクラスで実装します。次のようにImplementsステートメントでIMessageFilterインターフェイスの実装を指定します。
IMessageFilterインターフェイスは、3個の関数を持っています。Officeアプリ(PowerPoint)側(サーバー側)と、アドイン側(クライアント側)で使う両方の関数が定義されているため、アドインではサーバー側用のHandleInComingCall関数は使いません。ここでは、0を返すように記述しておきます。
Officeアプリがすぐに要求した処理を行えない場合、RetryRejectedCall関数が呼ばれます。-1を返すと処理をキャンセルし、0以上100未満の値を返すとすぐにリトライします。100以上の場合は、時間を置いた後にリトライを行います。
通常はダイアログを表示して、再試行するかユーザーに問いますが、今回の作るアドインではスライドショーの最中(プレゼン中)に発生するため、ユーザーに確認せず再試行するよう記述しておきます。ただし、Officeアプリ側が処理を拒否している場合は、処理をキャンセルして終わります。Officeアプリの応答は、引数のdwRejectTypeの値で判断します。
MessagePending関数は、Officeアプリの応答を待っている間に、何かしらのメッセージをアドインが受けた場合に呼ばれます。
以上で、実装部分は終わりです。
MessageFilterの登録
IMessageFilterインターフェイスを実装しただけでは意味がありません。非同期処理を行うときに、毎回 非同期で動いているスレッドからMessageFilterの登録を行います。登録および登録の解除は、CoRegisterMessageFilter関数を使います。
次のようにThisAddInクラスに定義を追加しましょう。
続いて、前回に記述した非同期で処理を行いOfficeアプリのUIを変更しているGetUserInfoメソッドを修正します。始めと終わりにCoRegisterMessageFilter関数を呼ぶように変更しました。
以上で、前回の修正は完了です。
マーカーの追加
それでは、アドインのメインとなる処理を書いていきましょう。まずは、マーカーの追加ボタンをクリックしたときの処理です。前回の内容では、ThisAddInクラスにAddMarkerメソッドを用意するところまで完了しています。
作成するアドインでは、オートシェイプの図形をスライドに貼り付け、スライドショー中にSkyDriveから写真をダウンロードし、オートシェイプの位置に表示するようにします。このマーカーとなるオートシェイプの追加処理を行います。
現在のスライドへ四角形のオートシェイプの追加は次のように記述できます。追加する位置と大きさは決め打ちにしています。
アドイン側で、マーカーとして追加したオートシェイプをほかのオートシェイプと区別できるようTagsプロパティを使って区別しておきます。また、今回作るアドインでは、最新の写真1枚のダウンロードにしか対応しませんので、既にマーカーが追加されていた場合は、削除して新たなマーカーを追加することにします。AddMarkerメソッドの全体のコードは次のようになります。
マーカーの追加ボタンがクリックされたとき、必ず編集できるスライドがあるとは限りませんので、その場合は例外が発生します。上記のコードでは、単純にすべての例外を無視するようにしています。
ここまでを実行してみましょう(図2)。マーカーの追加ボタンをクリックすると、オートシェイプが追加されましたか? このオートシェイプを移動や大きさを変えて写真を表示する位置を指定します。
スライドショー時の処理
次は、スライドショー中にスライドを切り替えた時の処理を記述します。Application.SlideShowNextSlideイベントで、スライドが表示されたときに処理を行います。
作成するアドインでは、プレゼン中にマーカーのあるスライドの直前まで表示したときに、SkyDriveから写真をダウンロードするようにします。
現在表示されているスライドの次のスライドにマーカーがあるかを確認し、ある場合に、SkyDriveから写真をダウンロードして次のスライドに追加します。コードは次のようになります。
写真のダウンロードと表示は、別途AddPictureメソッドを定義して、その中で処理します。
ただし、上記コードでは、少し不十分です。既にアドインによってダウンロードした写真がスライド上にある場合は、削除をここで行います。また、サインインしていない状態であれば、ダウンロードできませんので処理を抜けるようにします。サインイン処理は、SignInとGetUserInfoメソッドで記述していました。サインイン時に生成したLiveConnectClientオブジェクトを使ってサインイン状態か判断します。すべて処理を記述したコードは、次のようになります。
今回のアドインでは、1枚目のスライドにマーカーがある場合や、非表示のスライドがある場合は、考慮していません。
写真のダウンロード
SkyDriveから写真をダウンロードして、スライドに追加する処理を行いましょう。まず、AddPictureメソッドを定義します。このメソッドでは、スライドに追加するOfficeに対する操作があるため、MessageFilterの登録を行います。
さて、SkyDrive APIのおさらいです。サインインユーザーのSkyDrivet直下のフォルダーとファイルの一覧を取得するには次のURLへHTTP GETアクセスします。
- https://apis.live.net/v5.0/me/skydrive/files?access_token=ACCESS_TOKEN
また、指定したフォルダーの内容を取得するには次のURLへアクセスします。
- https://apis.live.net/v5.0/FOLDER_ID/files?access_token=ACCESS_TOKEN
指定したフォルダーから写真を取得してもいいのですが、今回のアドインでは、Windows Phoneで撮影した写真が自動でアップロードされる、カメラロール フォルダーから写真をダウンロードしましょう。このフォルダーは、Folder IDを使わず、特別なフォルダーとして次のようにURLのパスを記述できます。
- https://apis.live.net/v5.0/me/skydrive/camera_roll/files?access_token=ACCESS_TOKEN
さらに、次のようにURLのパラメーターを指定して、取得枚数と、並び順を指定できます。これによって、最新の1枚を選びます。また、filesではなくphotosとパスを指定すると写真ファイルのみ参照できます。
- https://apis.live.net/v5.0/me/skydrive/camera_roll/photos?access_token=ACCESS_TOKEN&limit=1&sort_by=updated&sort_order=descending
アドインでは上記のURLにアクセスするようにします。サーバーから受け取るデータは次のようなJSON形式のデータになります。この中から写真のダウンロードのためにsourceと、写真の幅(ピクセル)の取得にwidthとheightの値のみ使います。
写真のダウンロードまでをコードにします。LiveサービスにアクセスするLiveConnectClientクラスを用意していたので、これを使います。またJSONから値の取得にはJson.NETを利用します。
特別に難しいところはないと思います。続いてダウンロードした写真をスライドに追加します。
マーカーで示した範囲内に収まるように位置を計算し、slide.Shapes.AddPictureメソッドで写真を追加しています。また、アドインによって追加した写真とわかるように、pic.Tags.Addメソッド部分でタグ情報も付けています。
以上で、AddPictureメソッドの処理を記述できました。
動作の確認
ここまでで、アドインに必要な機能は、ほぼすべて実装できています。実行して動作を確認してみましょう。
- サインインボタンからサインインします。
- 新しいプレゼンテーションに、スライドを1枚追加します。
- 2枚目のスライドにマーカーを追加します(図3)。
ここまでの動作は、既に確認しています。それではスライドショーを開始してみましょう。1枚目のスライド表示された時点で、2枚目にマーカーがあるため写真のダウンロードが始まります。スライドを進めて写真が表示されれば成功です(図4)。
もちろん、SkyDriveに写真があることが前提になります。写真がない場合は、写真情報を取得しようとしている部分で例外が発生しています。Windows Phoneを持っている場合は、実際にSkyDriveへの自動アップロード設定を行い、写真を用意してみてください。または、コード中の「me/skydrive/camera_roll/photos」部分を「me/skydrive/photos」や、直接Folder IDを指定して、写真のある場所にアクセスするように変更してください。
図でもわかるように、写真の下にあるマーカーが見えていますので、マーカーは透明にするといいかもしれません。
サインアウト処理
少し残っている処理を書いていきましょう。サインアウトボタンをクリックしたときの処理は、SignOutメソッドで処理するようにしていました。今回利用しているLive ConnectのAPIでは、明示的なサインアウト用の操作は用意されていません。リボンのボタンの表示を変更し、LiveConnectClientオブジェクトの破棄のみ、ここでは行います。
アクセストークンの更新
先ほどの動作の確認では、きちんと写真がダウンロードできたかと思いますが、アクセストークンを使ったリソースのアクセス(今回はSkyDriveの写真へアクセス)は、有効期限があります。プレゼン中にアクセストークンの期限が切れるかもしれません。そこで、写真をダウンロードする前にアクセストークンの更新処理を行いましょう。
アクセストークンの更新には、リフレッシュトークンを追加います。この値は、サインイン時に取得するようにしていました。前回に用意したLiveConnectSessionオブジェクトのRefreshTokenプロパティから参照します。
新しいアクセストークンの取得は、次のURLにアクセスします。
- https://oauth.live.com/token?client_id=CLIENT_ID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN&redirect_uri=https://oauth.live.com/desktop
LiveAuthClientクラスに、次のコードを追記しましょう。GetSessionメソッドで得たLiveConnectSessionオブジェクトを渡すと、新しいLiveConnectSessionを得るメソッドです。
AddPictureメソッド内の、LiveConnectClient.Getメソッドを呼ぶ直前にコードを追記します。
ここでは単純に必ずアクセストークンを更新するようにしています。LiveConnectSessionオブジェクトは、期限情報も持っているためもう少し高度に処理することもできます。
おわりに
SkyDriveと連携したPowerPointアドインの開発は以上です。いかがでしたか。これまでの連載内容の一区切りということで、少しだけ実践的なアプリを作ってみました。ダウンロードに失敗したときの対応や、ユーザーが指定したフォルダーにある写真をダウンロードするようになど改善するポイントはいろいろあると思います。ここまで作ったみた方は、ぜひ改良もしてみてください。