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

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

この記事を読むのに必要な時間:およそ 9.5 分

はじめに

前回に引き続いて,PowerPointアドイン開発の後篇です。スライドショーの最中に最新の写真をSkyDriveからダウンロードしてスライドに表示するアドインを作ります図1)。

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

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

Officeアドインで非同期処理

前回でほとんどの準備を行いましたが,メインの処理を書く前に,準備をもうひとつ行います。Officeのアドインでスレッドを使った非同期処理を行う場合,一般的な決まった方法があり,今回はその内容を実装します。

ネットワークアクセスなど時間のかかる処理は,バックグランドで行い,完了時に結果をOfficeアプリ(今回の場合はPowerPoint)に反映したい場合があります。この結果を反映しようとしたとき,Officeアプリがすぐに処理を実行できない可能性があります。その場合,例外が発生するので,アドイン側で再試行などの処理をする方法,または,IMessageFilterインターフェイスを実装する方法があります。今回はIMessageFilterインターフェイスを実装する方法を採ります。詳しくは,Office でのスレッドのサポートなどを参照してください。

IMessageFilterの実装

まず,インターフェイスを用意します。Visual Studioのメニュー[プロジェクト][新しい項目の追加]から,インターフェイスを選択・追加します。IMessageFilter.vbというファイル名にします。そして,次のようにコードを編集してください。

IMessageFilter.vb

Imports System.Runtime.InteropServices
Imports System.Runtime.CompilerServices

<ComImport, ComConversionLoss, InterfaceType(1S), Guid("00000016-0000-0000-C000-000000000046")>
Public Interface IMessageFilter
    <PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
    Function HandleInComingCall(dwCallType As UInteger, htaskCaller As IntPtr, dwTickCount As UInteger, lpInterfaceInfo As IntPtr) As Integer

    <PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
    Function RetryRejectedCall(htaskCallee As IntPtr, dwTickCount As UInteger, dwRejectType As UInteger) As Integer

    <PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime)> _
    Function MessagePending(htaskCallee As IntPtr, dwTickCount As UInteger, dwPendingType As UInteger) As Integer
End Interface

このインターフェイスをThisAddInクラスで実装します。次のようにImplementsステートメントでIMessageFilterインターフェイスの実装を指定します。

ThisAddIn.vb

Public Class ThisAddIn
    Implements IMessageFilter

    '(省略)

    Public Function HandleInComingCall(dwCallType As UInteger, htaskCaller As IntPtr, dwTickCount As UInteger, lpInterfaceInfo As IntPtr) As Integer Implements IMessageFilter.HandleInComingCall
    End Function

    Public Function MessagePending(htaskCallee As IntPtr, dwTickCount As UInteger, dwPendingType As UInteger) As Integer Implements IMessageFilter.MessagePending
    End Function

    Public Function RetryRejectedCall(htaskCallee As IntPtr, dwTickCount As UInteger, dwRejectType As UInteger) As Integer Implements IMessageFilter.RetryRejectedCall
    End Function
End Class

IMessageFilterインターフェイスは,3個の関数を持っています。Officeアプリ(PowerPoint)(サーバー側)と,アドイン側(クライアント側)で使う両方の関数が定義されているため,アドインではサーバー側用のHandleInComingCall関数は使いません。ここでは,0を返すように記述しておきます。

Private Const SERVERCALL_ISHANDLED As Integer = 0
Public Function HandleInComingCall(dwCallType As UInteger, htaskCaller As IntPtr, dwTickCount As UInteger, lpInterfaceInfo As IntPtr) As Integer Implements IMessageFilter.HandleInComingCall
    Return SERVERCALL_ISHANDLED
End Function

Officeアプリがすぐに要求した処理を行えない場合,RetryRejectedCall関数が呼ばれます。-1を返すと処理をキャンセルし,0以上100未満の値を返すとすぐにリトライします。100以上の場合は,時間を置いた後にリトライを行います。

通常はダイアログを表示して,再試行するかユーザーに問いますが,今回の作るアドインではスライドショーの最中(プレゼン中)に発生するため,ユーザーに確認せず再試行するよう記述しておきます。ただし,Officeアプリ側が処理を拒否している場合は,処理をキャンセルして終わります。Officeアプリの応答は,引数のdwRejectTypeの値で判断します。

Private Const SERVERCALL_RETRYLATER As Integer = 2
Public Function RetryRejectedCall(htaskCallee As IntPtr, dwTickCount As UInteger, dwRejectType As UInteger) As Integer Implements IMessageFilter.RetryRejectedCall
    If dwRejectType = SERVERCALL_RETRYLATER Then
        Return 100
    Else
        Return -1
    End If
End Function

MessagePending関数は,Officeアプリの応答を待っている間に,何かしらのメッセージをアドインが受けた場合に呼ばれます。

Private Const PENDINGMSG_WAITDEFPROCESS As Integer = 2
Public Function MessagePending(htaskCallee As IntPtr, dwTickCount As UInteger, dwPendingType As UInteger) As Integer Implements IMessageFilter.MessagePending
    Return PENDINGMSG_WAITDEFPROCESS
End Function

以上で,実装部分は終わりです。

MessageFilterの登録

IMessageFilterインターフェイスを実装しただけでは意味がありません。非同期処理を行うときに,毎回 非同期で動いているスレッドからMessageFilterの登録を行います。登録および登録の解除は,CoRegisterMessageFilter関数を使います。

次のようにThisAddInクラスに定義を追加しましょう。

' (ファイル先頭に Imports System.Runtime.InteropServices も追加すること)
<DllImport("ole32.dll")>
Private Shared Function CoRegisterMessageFilter(lpMessageFilter As IMessageFilter, ByRef lplpMessageFilter As IMessageFilter) As Integer
End Function

続いて,前回に記述した非同期で処理を行いOfficeアプリのUIを変更しているGetUserInfoメソッドを修正します。始めと終わりにCoRegisterMessageFilter関数を呼ぶように変更しました。

Private Sub GetUserInfo(session As LiveConnectSession)
    Dim previousMessageFilter As IMessageFilter = Nothing
    CoRegisterMessageFilter(Me, previousMessageFilter) ' 登録

    Me.LiveConnectClient = New LiveConnectClient(session)
    Globals.Ribbons.MainRibbon.SignInButton.Enabled = False

    Try
        ' (ここの内容は前回と同じ)

    Catch ex As Exception
        ' (例外は無視)
        ' 再度サインインできるようボタンを有効化
        Globals.Ribbons.MainRibbon.SignInButton.Enabled = True
    Finally
        CoRegisterMessageFilter(Nothing, previousMessageFilter) ' 解除
    End Try
End Sub

以上で,前回の修正は完了です。

著者プロフィール

松江祐輔(まつえゆうすけ)

日本システムウエア株式会社 勤務。現在,ハードウェア設計・検証業務を担当。大学生・大学院生時代はベンチャー企業 有限会社ミレニアムシステムズにプログラマーとして従事。趣味はプログラミング。好きな言語はVisual Basic。Microsoft MVP for Windows Live Platform(Jul 2010 - Jun 2011),Windows Live(Jul 2011 - Jun 2013)。

URL:http://katamari.jp

コメント

コメントの記入