使ってみよう! Windows Live SDK/API

第53回作ってみようSkyDrive連携PowerPointアドイン(前篇)

はじめに

今回と次回は、少し予告していたように、SkyDrive APIを利用した簡単なアプリ開発として、SkyDriveと連携するPowerPointアドインを作ります図1⁠。

図1 SkyDriveと連携するPowerPointアドイン 図1 SkyDriveと連携するPowerPointアドイン

これまでの連載で紹介してきたように、Live SDKおよびAPI群のLive Connectを利用すると、SkyDrive・Hotmail・Messengerなどのサービスと統合したアプリ開発が可能です。このLive Connectを利用したアドインを作ります。前回までの内容を見ていない方でも、とりあえずコードを記述していけば作れるよう構成していますので、ぜひトライしてみてください。

前回に少しふれたLive SDK 5.1 Previewは既に正式にリリースされ利用できるようになっています。また、開発者向けのWebサイトLive Connect デベロッパーセンターの内容もリニューアルされ、日本語対応も進んでいます。

図2 Live Connect デベロッパーセンター
図2 Live Connect デベロッパーセンター

また、今回アプリからアクセスするSkyDriveは、4月24日に、予告されていたWindows・Mac向けのクライアントアプリのリリースと、サービス内容が刷新され、使いやすいサービスとなっています。詳しくは、Building Windows 8や、各種ニュース記事などを確認してください。

PowerPointアドインの開発

今回は、SkyDriveと連携したMicrosoft Office PowerPointのアドインを作ります。

Windows PhoneはSkyDriveとシームレスに連携していて、簡単に撮影した写真をSkyDriveにアップロードできます。通常、カメラロールというフォルダーに写真はアップロードされます。このアップロードされた最新の写真1枚を、スライドショーの最中にスライドに追加して表示するアドインを作ります。

プレゼンテーション中にWindows Phoneを使ってデモ画面や会場を撮影してSkyDriveにアップロードすると、スライドにその場で反映され、少しだけ凝ったプレゼンテーションが可能かもしれませんね。もちろん、写真のアップロードは、iPhone・iPadのSkyDriveアプリやSkyDrive.comを利用して構いません。

アドインでは、あらかじめスライドに写真をダウンロードして表示する場所にマーカーとなるオートシェイプの図形を配置して使います。

開発環境

開発にはVisual Studioを使用します。Officeアドインを作成するにはVisual StudioのProfessional以上が必要です。今ならVisual Studio 11 Betaを使ってみるのも手かもしれません。

ここでは、Visual Studio 11 BetaおよびPowerPoint 2010を使用し、言語はVisual Basicを選択します。

アプリの登録

はじめに、作成するアプリ(ここではアドイン)をあらかじめLiveサービスに登録します。登録にはLiveアカウントが必要です。Live Connectデベロッパーセンターマイアプリ図3へアクセスします。

図3 マイ アプリケーション
図3 マイ アプリケーション

「アプリケーションの作成」から新しいアプリを登録します図4⁠。はじめてアプリを作る場合は、最初からこのページが表示されています。

図4 アプリの登録
図4 アプリの登録

アプリケーション名と言語を入力し、使用条件の確認後、同意するボタンをクリックします。登録が完了すると、Liveサービスにアクセスするときに必要な、クライアントIDクライアントシークレットが得られます。さらに、⁠アプリケーションの設定ページ」に移動し必要な情報を入力しましょう図5⁠。作成するアドインでは、クライアントIDのみ使います。

図5 アプリの設定ページ
図5 アプリの設定ページ

今回のようなデスクトップクライアント用のアプリ、また、Webアプリやモバイル クライアントアプリを開発する場合は、このマイアプリのページを利用します。モバイル クライアントアプリの場合は、API設定でモバイル クライアントアプリを選択する必要があります。

プロジェクトの作成

それでは、順に開発していきましょう。

メニューの[ファイル][新規作成][プロジェクト]から、テンプレート[Visual Basic][Office][2010]のPowerPoint 2010アドインを選択し、プロジェクトを作成します図6⁠。ここでは名前をSamplePowerPointAddInとしています。

図6 プロジェクトの新規作成
図6 プロジェクトの新規作成

プロジェクトには、ThisAddIn.vbファイルがあり、ThisAddInクラスが定義されています。ここにアドインの処理を記述していきます。

Json.NETのインストール

Live Connectを利用する際にJSONデータを扱いますが、今回はJSONの処理にJson.NETを使います。

Visual Studio 11の場合、パッケージマネージャーのNuGetを利用してインストールできます。メニューの[プロジェクト][Manage NuGet Packages]からウィンドウを開いて、右上のOnlineの検索から「json.net」を検索し、Json.NETをインストールします図7⁠。

図7 NuGetでJson.NETのインストール
図7 NuGetでJson.NETのインストール

または、CodePlexからダウンロードし、Bin/Net40フォルダー内のNewtonsoft.Json.dllを、メニューの[プロジェクト][参照の追加]から、参照する方法でも構いません。

リボンの追加

はじめに、見た目の部分を作ります。プロジェクトへ、リボンを追加しましょう。ソリューションエクスプローラーで、プロジェクトが選択されている状態で、メニューの[プロジェクト][新しい項目の追加]より、⁠リボン(ビジュアルなデザイナー⁠⁠」を選択・追加します図8⁠。名前はMainRibbon.vbとしました。

図8 リボンの追加
図8 リボンの追加

図9のようにリボンが表示され、コントロールの追加や設定ができます。

図9 リボンの表示
図9 リボンの表示

リボンのコントロールを追加しましょう。ツールボックスの「Office リボン コントロール」からButtonMenuを配置します。ButtonとMenuともうひとつButtonを順にGroup1の中へドラッグ&ドロップし、さらにMenu1の▼部分をクリックして中にButtonをドラッグ&ドロップします。

Group1の中にある、Button 2個とMenu 1個を順にクリックして選択し、プロパティウィンドウの「ControlSize」「RibbonControlSizeLarge」に変更します。ここまで行うと図10のようになっています。

図10 リボンコントロールの配置
図10 リボンコントロールの配置

配置したボタンとメニューは、それぞれ、サインイン・サインアウト・マーカーの追加用です。サインイン・サインアウトは表示・非表示を切り替えてサインアウトはメニュータイプのUIを利用することにします。

ImageとLabelプロパティも編集して図11のようにします。画像は適当なものを用意してください。TabのLabelプロパティも編集します。

MenuのImageとLabelは、サインインしたユーザーの名前とアイコンの表示用のため、編集不要です。

図11 編集後のリボン
図11 編集後のリボン

また、MenuのVisibleプロパティをFalseに設定し、最初に表示したとき見えないようにしておきます。

ButtonとMenuの名前も変更しておきましょう。この後のコードでは、SignInButton・SignOutButton・MarkerButtonをボタン名、SignOutMenuをメニュー名として用います。

コードの編集

リボンのボタンをクリックしたときの処理は、ThisAddInクラスで行うようにします。ここで、リボン側のコードを記述しておきます。

まず、ThisAddIn.vbを編集し、次のようにメソッドを用意します。最初から記述されているThisAddIn_StartupとThisAddInShutdownは使いません。

Public Class ThisAddIn
    Private Sub ThisAddIn_Startup() Handles Me.Startup
    End Sub

    Private Sub ThisAddIn_Shutdown() Handles Me.Shutdown
    End Sub

    ' 以下の3個のメソッドを追加
    Sub SignIn()
    End Sub

    Sub SignOut()
    End Sub

    Sub AddMarker()
    End Sub
End Class

リボン側で、追加したメソッドを呼ぶようにします。MainRibbon.vbのコードを次のように編集します。このとき、リボンのボタンをダブルクリックすると自動でボタンクリック時のコードが記述されます。

Imports Microsoft.Office.Tools.Ribbon
Public Class MainRibbon
    Private Sub SignInButton_Click(sender As Object, e As RibbonControlEventArgs) Handles SignInButton.Click
        Globals.ThisAddIn.SignIn()
    End Sub

    Private Sub SignOutButton_Click(sender As Object, e As RibbonControlEventArgs) Handles SignOutButton.Click
        Globals.ThisAddIn.SignOut()
    End Sub

    Private Sub MarkerButton_Click(sender As Object, e As RibbonControlEventArgs) Handles MarkerButton.Click
        Globals.ThisAddIn.AddMarker()
    End Sub
End Class

上記コードでは、ThisAddInクラスに追加した各メソッドを呼び出しています。以上でリボンの編集内容は完了です。

認可画面フォームの作成

Live Connectは、認可のプロトコルOAuthを利用して、ユーザーからSkyDriveなどのリソースへのアクセス許可を得ます。その際に、Live Connectで用意されている認可画面図12を表示し、ユーザーの認証・認可処理を行います。

図12 認可画面
図12 認可画面

次は、この認可画面の部分を作ります。認可画面はWebページのため、Webブラウザーコントロールを使って表示します。Webブラウザーコントロールを配置するフォームをプロジェクトに追加しましょう。

新しい項目の追加で、⁠Windows フォーム」を選択・追加します図13⁠。ここではファイル名を、ConsentForm.vbとしています。

図13 Windowsフォームの追加
図13 Windowsフォームの追加

ツールボックスから、WebBrowserを追加したフォーム上へ、ドラッグ&ドロップします図14⁠。

図14 WebBrowserコントロールの配置
図14 WebBrowserコントロールの配置

コントロール名を適当なものに変更しましょう。本稿では、MainWebBrowserと変更し、後述のコードで使います。

認可画面の表示

Live Connectのおさらいです。認可画面図12のWebページのURLは次の通りです。ここにアクセスし、ユーザーからアプリに対してリソースへのアクセス許可を得ます。

  • https://oauth.live.com/authorize?client_id=CLIENT_ID&scope=SCOPES&response_type=code&redirect_uri=https://oauth.live.com/desktop

これは、OAuth 2.0のAuthorization Code Grant FlowというAuthorization Code認可コードを使用する方法を使います。

作成するフォームでは、生成時にClient IDクライアントIDと認可の内容を示すScopeスコープを渡し、その認可画面をWebブラウザーコントロールで表示するようにします。

プロトコルに関する部分は、第45回第47回に少し詳しくありますので、興味のある方は参照してください。

ConsentForm.vbに記述するコードは次のようになります。

Public Class ConsentForm
    Private RedirectUri As Uri
    Private ClientId As String
    Private Scopes As IEnumerable(Of String)

    Public Sub New(clientId As String, scopes As IEnumerable(Of String))
        InitializeComponent()

        ' コンストラクタで Client ID と Scope を受け取り Private メンバへ格納
        Me.RedirectUri = New Uri("https://oauth.live.com/desktop")
        Me.ClientId = clientId
        Me.Scopes = scopes
    End Sub

    Private Sub ConsentForm_Load(sender As Object, e As EventArgs) Handles Me.Load
        ' フォーム Load 時に Web ブラウザーコントロールに 認可画面を表示
        Dim uri = New Uri(String.Format("https://oauth.live.com/authorize?locale=ja&client_id={0}&scope={1}&response_type=code&redirect_uri={2}",
                                        Me.ClientId, String.Join("+"c, Scopes), Me.RedirectUri))
        Me.MainWebBrowser.Navigate(uri)
    End Sub
End Class

Webブラウザーコントロール内の認可画面をユーザーが操作し、アプリの要求にユーザーが承諾した場合、別のURLへリダイレクトされます。そのURLは次のようになります。

  • https://oauth.live.com/desktop?code=[AuthorizationCode]

このcodeパラメーターに認可コードが含まれています。この値を参照できるようコードに追記します。

Private _Parameters As New Dictionary(Of String, String)
ReadOnly Property Parameters As IDictionary(Of String, String)
    Get
        Return _Parameters
    End Get
End Property

Private Sub MainWebBrowser_Navigated(sender As Object, e As Windows.Forms.WebBrowserNavigatedEventArgs) Handles MainWebBrowser.Navigated
    If e.Url.Host <> RedirectUri.Host OrElse e.Url.AbsolutePath <> RedirectUri.AbsolutePath Then
        ' https://oauth.live.com/desktop 以外のアドレスの場合処理を抜ける
        Exit Sub
    End If

    ' URL のクエリーを取得
    Me._Parameters.Clear()
    Dim param = e.Url.Query.Substring(1).Split({"&"c, "="c})
    For i = 0 To param.Length - 1 Step 2
        Dim val = param(i + 1)
        Me._Parameters.Add(param(i), param(i + 1))
    Next

    ' フォームを閉じる
    Me.DialogResult = Windows.Forms.DialogResult.OK
    Me.Close()
End Sub

上記のコードでは、ページがhttps://oauth.live.com/desktopへ遷移した場合、URLのクエリー部分を取得し、フォームを閉じます。認可コードを含むクエリーの値は、プロパティとして参照できるようにしています。

フォームに記述するコードは以上です。

Live Connect クラスの作成

さて、ここでLive Connectに関連した処理を行うクラスをいくつか作っていきます。作成するクラスは4つです。Live SDKのManaged APIにあるクラスと似せて作っていますが、処理内容は少し異なるので注意してください。

新しくクラスを作るには、新しい項目の追加からクラスを選択・追加して行います。

LiveConnectSession

OAuthのAuthorization Code Grant Flowでは、認可コードを使って、アクセストークンという文字列を得ます。アクセストークンを使うと、認可されたリソースへのアクセスができるようになります。

はじめに、このアクセストークンなど認可情報を格納するLiveConnectSessionクラスを用意します。

LiveConnectSession.vb
Namespace Live
    Public Class LiveConnectSession
        Private _AccessToken As String
        ReadOnly Property AccessToken As String
            Get
                Return _AccessToken
            End Get
        End Property

        Private _RefreshToken As String
        ReadOnly Property RefreshToken As String
            Get
                Return _RefreshToken
            End Get
        End Property

        Private _Expires As DateTimeOffset
        ReadOnly Property Expires As DateTimeOffset
            Get
                Return _Expires
            End Get
        End Property

        Private _Scopes As IEnumerable(Of String)
        ReadOnly Property Scopes As IEnumerable(Of String)
            Get
                Return _Scopes
            End Get
        End Property

        Friend Sub New(accessToken As String, refreshToken As String, expires As DateTimeOffset, scopes As IEnumerable(Of String))
            Me._AccessToken = accessToken
            Me._RefreshToken = refreshToken
            Me._Expires = expires
            Me._Scopes = scopes
        End Sub
    End Class
End Namespace

アクセストークンのAccessTokenプロパティ、その有効期限を示すExpiresプロパティ、許可を得ているスコープを示すScopesプロパティ、そして、アクセストークンの更新に必要なリフレッシュトークンと呼ばれる文字列のRefreshTokenプロパティから成っています。

LiveAuthClient

次に、認証・認可に関する処理を行うクラスを作ります。作成した認可画面フォームの表示はこのクラスから行います。また、得られた認可コードからアクセストークンを取得し、完了後にイベントを起こすという仕組みにします。

アクセストークンの取得は、次のURLにアクセスします。

  • https://oauth.live.com/token?client_id=CLIENT_ID&code=AUTHORIZATION_CODE&grant_type=authorization_code&redirect_uri=https://oauth.live.com/desktop

クラスは、次のように記述します。

LiveAuthClient.vb
Imports System.Threading.Tasks
Imports System.Net
Imports Newtonsoft.Json.Linq

Namespace Live
    Public Class LiveAuthClient
        Private ClientId As String
        Private RedirectUri As Uri

        Event InitializeCompleted As EventHandler(Of LoginCompletedEventArgs)

        Sub New(clientId As String)
            ' コンストラクタでアプリのクライアント ID を渡す
            Me.ClientId = clientId
            Me.RedirectUri = New Uri("https://oauth.live.com/desktop")
        End Sub

        ' 認可画面フォームの表示、アクセストークン取得後イベントを起こす
        Sub InitializeAsync(scopes As IEnumerable(Of String))
            Using form = New ConsentForm(Me.ClientId, scopes)
                If form.ShowDialog <> Windows.Forms.DialogResult.OK OrElse
                   Not form.Parameters.ContainsKey("code") Then
                    Exit Sub
                End If

                Task.Factory.StartNew(Of LiveConnectSession)(
                        Function()
                            Return GetSession(form.Parameters("code"))
                        End Function) _
                    .ContinueWith(
                        Sub(t As Task(Of LiveConnectSession))
                            RaiseEvent InitializeCompleted(Me, New LoginCompletedEventArgs(t.Result))
                        End Sub)
            End Using
        End Sub

        ' 認可コードからアクセストークンを取得
        Private Function GetSession(code As String) As LiveConnectSession
            Dim uri = New Uri(String.Format("https://oauth.live.com/token?client_id={0}&code={1}&grant_type=authorization_code&redirect_uri={2}",
                                           Me.ClientId, code, Me.RedirectUri))
            Dim client = New WebClient
            Dim json = client.DownloadString(uri)

            Dim o = JObject.Parse(json)

            If o("error") IsNot Nothing Then
                Return Nothing
            End If

            Dim session As New LiveConnectSession(
                o("access_token").ToString(),
                o("refresh_token").ToString(),
                New DateTimeOffset(Now.ToUniversalTime).AddSeconds(o("expires_in").ToObject(Of Integer)),
                o("scope").ToString.Split(" "c))

            Return session
        End Function
    End Class
End Namespace

GetSessionメソッドでアクセストークンを取得しています。サーバーからの応答は次のようなJSONデータになっています。コードでは、Json.NETのライブラリーを使用して、必要な情報を取り出し、LiveConnectSessionオブジェクトを作っています。

{
    "access_token": "xxxxx", 
    "expires_in": 3600, 
    "refresh_token": "xxxxx", 
    "scope": "wl.offline_access wl.skydrive", 
    "token_type": "bearer"
}

ちなみに、エラーの場合は、次のような構成のデータです。

{
    "error": "invalid_grant", 
    "error_description": "The provided value for the input parameter 'code' is not valid."
}

LoginCompletedEventArgs

続いて、LiveConnectClientクラスで使用しているイベント用のクラスを作ります。

LoginCompletedEventArgs.vb
Namespace Live
    Public Class LoginCompletedEventArgs
        Inherits EventArgs

        Private _Session As LiveConnectSession
        ReadOnly Property Session As LiveConnectSession
            Get
                Return _Session
            End Get
        End Property

        Sub New(session As LiveConnectSession)
            _Session = session
        End Sub
    End Class
End Namespace

LiveConnectClient

最後は、REST APIの呼び出しを行うクラスです。Live Connectでは、REST APIを利用して、SkyDriveなどのリソースへアクセスします。この時にアクセストークンを使います。

REST APIは、これまでの連載で何度もでてきています。SkyDriveのフォルダーとファイル一覧であれば、次のURLへHTTP GETアクセスして取得します。

  • https://apis.live.net/v5.0/me/skydrive/files?access_token=ACCESS_TOKEN

今回はHTTP GETメソッドしか使いませんので、次のようなクラスにしました。

LiveConnectClient.vb
Namespace Live
    Public Class LiveConnectClient
        Private _Session As LiveConnectSession
        ReadOnly Property Session As LiveConnectSession
            Get
                Return _Session
            End Get
        End Property

        Public Sub New(session As LiveConnectSession)
            _Session = session
        End Sub

        Function [Get](path As String) As String
            Dim uri = New Uri("https://apis.live.net/v5.0/" &
                    path &
                    If(path.Contains("&"), "&", "?") &
                    "access_token=" & Me.Session.AccessToken)

            Dim client = New Net.WebClient With {.Encoding = System.Text.Encoding.UTF8}
            Return client.DownloadString(uri)
        End Function
    End Class
End Namespace

以上で、必要なLive Connect関連のクラスが準備できました。

サインインユーザーの表示

ここで、今回の最後の内容です。ここまでで用意したクラスを使って、サインインしたユーザーの表示までしてみましょう。リボンのサインインボタンをクリックすると、認可画面フォームを表示します。ユーザーの認証と認可処理を行った後、サインインしたユーザーをリボンに表示します。

サインイン

ThisAddInクラスにあるSingInメソッドを編集します。

' (ファイルの先頭に Imports SamplePowerPointAddIn.Live を記述すること)

Private Const ClientId As String = "XXXXX" ' 登録したアプリのクライアント ID

Sub SignIn()
    Dim client = New LiveAuthClient(ThisAddIn.ClientId)
    AddHandler client.InitializeCompleted,
        Sub(s, e)
            If e.Session Is Nothing Then
                Exit Sub
            End If
            Dim t = New Threading.Thread(AddressOf GetUserInfo)
            t.SetApartmentState(Threading.ApartmentState.STA)
            t.Start(e.Session)
        End Sub
    client.InitializeAsync(New String() {"wl.skydrive", "wl.offline_access"})
End Sub

LiveAuthClientオブジェクトを生成し、InitializeAsyncメソッドを呼びます。生成時に登録したアプリのクライアントIDを指定し、メソッドを呼ぶときにスコープを指定します。作成するアドインで必要なスコープは、SkyDriveのアクセスと、ユーザーがオフライン時のアクセスです。値は、wl.skydrivewl.offline_accessです。wl.offline_accessを指定するとリフレッシュトークンの取得ができるようになります。

InitializeAsyncメソッドで、認可画面フォームを表示し、ユーザーが認可処理を行うとInitializeCompletedイベントが発生します。上記コードでは、イベント発生時にThreadを生成し、GetUserInfoメソッドを呼んでいます。次はこのGetUserInfoメソッドを追記しましょう。

GetUserInfoメソッドでは、LiveConnectSessionオブジェクトを付け取り、LiveConnectClientオブジェクトを通してREST APIにアクセスし、サインインしたユーザー情報を取得します。また、リボンコントロールの表示・非表示などの操作を行います。

' (ファイルの先頭に Imports Newtonsoft.Json.Linq を記述すること)

Private LiveConnectClient As LiveConnectClient

Private Sub GetUserInfo(session As LiveConnectSession)
    Me.LiveConnectClient = New LiveConnectClient(session)
    Globals.Ribbons.MainRibbon.SignInButton.Enabled = False

    Try
        ' サインインしたユーザーの表示アイコンをダウンロード
        Dim url = "https://apis.live.net/v5.0/me/picture?access_token=" & session.AccessToken
        Dim client = New Net.WebClient
        Dim file = System.IO.Path.GetTempFileName
        client.DownloadFile(url, file)

        ' サインインしたユーザー情報を取得
        Dim result = LiveConnectClient.Get("me")

        ' UI 操作
        Dim o = JObject.Parse(result)
        Globals.Ribbons.MainRibbon.SignOutMenu.Label = o("name").ToString
        Globals.Ribbons.MainRibbon.SignOutMenu.Image = New System.Drawing.Bitmap(file)
        Globals.Ribbons.MainRibbon.SignOutMenu.Visible = True
        Globals.Ribbons.MainRibbon.SignInButton.Visible = False

    Catch ex As Exception
        ' (例外は無視)
    End Try
End Sub

コード中にある次のURLにアクセスすると、サインインユーザーの表示アイコンを取得できます。

  • https://apis.live.net/v5.0/me/picture?access_token=ACCESS_TOKEN

また、LiveConnectClientオブジェクトを使ってREST APIを呼びます。Getメソッド内部では、次のURLにアクセスしています。

  • https://apis.live.net/v5.0/me?access_token=ACCESS_TOKEN

この結果は、次のようなJSONデータです。

{
   "id": "xxxxx", 
   "name": "梓 中野", 
   "first_name": "梓", 
   "last_name": "中野", 
   "gender": null, 
   "locale": "en_US"
}

コードでは、以上の得られたデータを、リボンのボタンに反映しています。

さて、ここまでを実行してみましょう。実行(⁠⁠デバッグ][デバッグ開始⁠⁠)すると、PowerPointが起動し、リボンに作成したタブ「写真の追加」が表示されているはずです。サインインボタンをクリックすると、認可画面が表示されます。サインイン後には、アイコンとユーザー名が表示されたでしょうか。

このサインイン処理は、次回に少しOfficeアドインの作法に従って書き直します。

おわりに

今回はここまでです。いかがでしたか。今回はほとんど準備段階となってしまいましたが、最後にLive Connectでユーザー情報を取得するところまで行いました。次回はメインとなる部分を実装していきましょう。また、Officeアドインでの非同期処理についても扱います。

おすすめ記事

記事・ニュース一覧