ささいなことですが。

Windowsアプリテスト自動化ライブラリFriendly開発者の日記です。

Friendly.PinInterface.1.3.1をリリースしました。

Friendly.PinInterface.1.2.01.3.1*1をリリースしました。
内容は、対象のオブジェクトがSystem.__ComObjectだった場合の対応です。
前回の話はこの前振りでした。

Friendly.PinInterfaceって何?

Friendlyの操作に型を与え、実装時にインテリセンスが使えるようにすることと、タイポを防ぐことが目的です。(最終は別プロセス操作になるのでどうしても型安全とまでは行かないですが)
vshtc/Friendly.PinInterface · GitHub
これはVSハッカソン倶楽部というコミュニティと共同で作成しています。

例えば、操作対象がSystem.Windows.Forms.Formだった場合以下のようなインターフェイスを定義するとその型で操作できます。

interface IForm
{
    string Text { get; set; }
    void Activate();
}
[TestMethod]
public void Test()
{
    var app = new WindowsAppFriend(Process.Start(Target.Path));
    AppVar src = app.Type().System.Windows.Forms.Application.OpenForms[0];
    var form = src.Pin<IForm>();
    form.Activate();
}

インテリセンスが効きます。
f:id:ishikawa-tatsuya:20150723230052p:plain

でも、あんまり使っていませんでした。と言うのはテスト時は最終的にはアプリケーションドライバでラップするので、その内側で使うものに関してはdynamicでも別にいいじゃんってなっていました。自動で生成するなら別ですが、わざわざインターフェイス定義するの面倒ですしね。

実はCOMの操作にピッタリだったのです!

きょう再発見しましたw
COMって全部インターフェイスで提供されています。インターフェイスを自分で定義するのは面倒でも既にインターフェイスが提供されているなら話は別です。それともう一つPinInterfaceには特徴があって、戻り値がインタフェースの場合は、それも対象アプリ内のオブジェクトのプロキシとして返ってきます。前回のを書き直すと以下のようになります。

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Codeer.Friendly.Dynamic;
using Codeer.Friendly.Windows;
using System.Diagnostics;
using System.Windows.Forms;
using System.Threading;
using mshtml;
using Codeer.Friendly;
using VSHTC.Friendly.PinInterface;

namespace ComTest
{
    [TestClass]
    public class Sample
    {
        WindowsAppFriend app;

        [TestInitialize]
        public void TestInitialize()
        {
            app = new WindowsAppFriend(Process.Start("ComTarget"));
        }

        [TestCleanup]
        public void TestCleanup()
        {
            Process.GetProcessById(app.ProcessId).CloseMainWindow();
        }

        [TestMethod]
        public void Test()
        {
            var web = app.Type().System.Windows.Forms.Application.OpenForms[0].Controls[0];
            web.Url = new Uri("http://www.codeer.co.jp/");
            while ((WebBrowserReadyState)web.ReadyState != WebBrowserReadyState.Complete)
            {
                Thread.Sleep(10);
            }

            var iframeWindow = web.Document.Window;
            AppVar com = iframeWindow.DomWindow;

            //IHTMLWindow2にピン止めする
            //実体は対象アプリ内にある
            var win = com.Pin<IHTMLWindow2>();
            //IHTMLDocument2で返ってくる
            //コピーではなく実体は対象アプリ内にある
            var doc = win.document.selection;
            //IHTMLSelectionObjectで返ってくる
            var sel = win.document.selection;
            var type = sel.type;
        }
    }
}

ちなみにこのコードは以下からダウンロードできるので、実際に動かしたり、インテリセンスを使ったりしてみてくださいね。
Ishikawa-Tatsuya/Sample_PinInterface_COM · GitHub

PinInterfaceなら、実行時に型が分かるから、OperationTypeInfoを書かなくてもいい

そう、それで今回の対応は何かというと、対象のオブジェクトがSystem.__ComObjectの場合は固定されている型の情報を使って内部的にOperationTypeInfoを作ってCOMオブジェクトを操作できるようにするというものでした。

//selはSystem.__ComObjectだけど、
//IHTMLSelectionObjectだと分かっているのでライブラリの内部で
//OperationTypeInfoを生成して操作できる。
var type = sel.type;

でもCOM操作なんていつやるの?

あのアプリを操作する時ですよ。アレです。アレ。その詳細は、7/25のめとべや大阪#31 - めとべや大阪 | Doorkeeperで話しますw(LTだけど)
気になる方は聞きに来てくださいね。

*1:すみません。微調整入って1.3.1になりました