Silverlight 4 RC の COM 機能を使って Excel に twitter 検索結果を流し込む

Silverlight 4 から Out of Browser でCOMオートメーション機能が使えるようになった。これにより Excel などの外部アプリケーションを直接呼び出し操作できるようになりました。
Silverlight 4 の出荷候補版も出たので試しに COM による Excel 連携機能を実装してみることにした。今回は作ったのは、twitter で検索した結果を Excel シートに流し込むアプリです。

開発環境

Windows 7 上の Visual Studio 2010 RC と Microsoft Silverlight 4 Tools for Visual Studio 2010 RC による

ポイント

COMオートメーション機能

まず COMオートメーションの機能は、βからRCになり仕様が変わっています。
AutomationFactory.CreateObject にオブジェクト名を指定してインスタンスを生成します。下記ではさらのExcelを表示して、ワークブックを追加しその最初の(アクティブ)シートを取得しています。

using System.Runtime.InteropServices.Automation;

・・・

dynamic excel = AutomationFactory.CreateObject("Excel.Application");
excel.Visible = true;

dynamic workbook = excel.workbooks.Add();
dynamic excelSheet = excel.ActiveSheet;

※ dynamic を使用するには Microsoft.CSharp の参照追加が必要です。

twitter検索

ボタンのクリックイベントでsearchTextBoxに入力されてたTextでtwitter search API をたたきます。
DataContractJsonSerializer を使って、レスポンスストリームから直接 JSON を C# 上の構造体クラスのデータオブジェクトに変換しています。ToExcelに結果オブジェクトを渡して処理をします(これは後述)。

private void button1_Click(object sender, RoutedEventArgs e) {
  if (searchTextBox.Text.Length == 0) return;
  string word = System.Windows.Browser.HttpUtility.UrlEncode(searchTextBox.Text);
  HttpWebRequest webreq = (HttpWebRequest)WebRequest.Create("http://search.twitter.com/search.json?q=" + word);
  webreq.Method = "GET";
  webreq.BeginGetResponse(new AsyncCallback(WebResponseCallback), webreq);
}

private void WebResponseCallback(IAsyncResult ar) {
  try {
     HttpWebRequest request = (HttpWebRequest)ar.AsyncState;
     HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(ar);
     Stream responseStream = response.GetResponseStream();

     TwitterResult result;
     DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(TwitterResult));
     result = ser.ReadObject(responseStream) as TwitterResult;
     ToExcel(result);
  } catch (Exception) {
  }
}

TwitterResult, TwitterResultItem に twitter のレスポンスデータ表現を定義しています。

using System.Runtime.Serialization;

    [DataContract]
    public class TwitterResult
    {
        [DataMember(Name = "results")]
        public IList<TwitterResultItem> Results { get; set; }
    }

    [DataContract] 
    public class TwitterResultItem
    {
        [DataMember(Name = "profile_image_url")]
        public string ProfileImageUrl { get; set; }

        [DataMember(Name = "created_at")]
        public string CreatedAt { get; set; }

        [DataMember(Name = "from_user")]
        public string FromUser { get; set; }

        [DataMember(Name = "to_user_id")]
        public string ToUserId { get; set; }

        [DataMember(Name = "text")]
        public string Text { get; set; }

        [DataMember(Name = "id")]
        public long Id { get; set; }

        [DataMember(Name = "from_user_id")]
        public string FromUserId { get; set; }

        [DataMember(Name = "to_user")]
        public string ToUser { get; set; }
        
        [DataMember(Name = "geo")]
        public string Geo { get; set; }

        [DataMember(Name = "iso_language_code")]
        public string IsoLanguageCode { get; set; }
        
        [DataMember(Name = "source")]
        public string Source { get; set; }

        public TwitterResultItem() {
        }
    }
データをExcelに表示

GetExcelSheet() が返すのは前述の excelSheet です。やっていることは 2行目2列目のセルを選択し、さらに行全体を選択します。その位置に対して行を挿入します。
挿入した位置の行にTwitterのユーザーIDと呟き内容を1,2列目にセットします。
以上を取得したつぶやき数だけ繰り返してします。

private void ToExcel(TwitterResult result) {
  dynamic sheet = GetExcelSheet();

  foreach (TwitterResultItem item in result.Results) {
    dynamic range = sheet.Cells[2, 2];
    dynamic row = range.EntireRow;
    row.Insert(0, false);

    range = sheet.Range(sheet.Cells[2, 1], sheet.Cells[2, 2]);

    string[] values = { item.FromUser, item.Text };
    range.value = values;
  }
}

まとめ

COM連携
  • 一般的な .NET の Window アプリケーションように、Office アプリケーションの Interop ライブラリを参照することで事前バインディングな実装をすることができません。dynamic 変数で受ける必要があります。
  • COMオートメーションによる操作はInvokeしてメインスレッドに委譲しなくてよい。対象先はプロセスが異なるのでそもそも問題にはならないのかもしれない。
Out of Browserでのデバッグ

デバッグにはまずWebプロジェクトで実行後にインストール。その後スタートアップを非Webプロジェクトに設定してデバッグを開始します。