ささいなことですが。

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

OperationTypeInfoとSystem.__ComObject

Friendlyのマニアックな機能にOperationTypeInfoというものがあります。
僕以外に知っている人は、世界に10人いないでしょうねw
OperationTypeInfo - 株式会社Codeer (コーディア)

例えば、こんなクラスを操作するとして、

class Parent
{
    int data;
}

class Child : Parent
{
    //同名の変数が存在する
    int data;

    //オーバーロードされた関数が存在する
    public void Test(Form form)
    {
        //...処理
    }
    
    public void Test(Control control)
    {
        //...処理
    }
}

普通に書くと

//子供しか取れない
int data = child.data;
//例外発生
//どっちのメソッドか分からない
child.Test(null);

で、これに対応するためにOperationTypeInfoがあります。操作時の型と引数の型を確定させるのです。Asyncと同じく、引数のどこかに混ぜてください。
あ、propertyとかfieldの場合も()つけて渡せばOKです。これもマニアックな話ですねw

//対象クラスを指定
int data = child.data(new OperationTypeInfo(typeof(Parent).FullName);
//対象クラスと引数を指定
child.Test(new OperationTypeInfo(typeof(Parent).FullName, typeof(Form).FullName);

System.__ComObjectにも利用

対象プロセス内のオブジェクトを操作するときにCOMのオブジェクトが混ざっていることがあります。COMのオブジェクトは型が確定しているものもありますが、大部分はGetType()をするとSystem.__ComObjectというクラスが取得されます。Friendlyは対象プロセス内ではリフレクションを元に処理を実行するので、これでは本当に操作したい型が取れずにエラーになってしまいます。ココでもOperationTypeInfoが役に立ちます。

例えば、WebBrowserを使っているこんなFormがあって

using System.Windows.Forms;
namespace ComTarget
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
            var web = new WebBrowser();
            web.Dock = DockStyle.Fill;
            Controls.Add(web);
        }
    }
}

Friendlyで操作すると

var app = new WindowsAppFriend(Process.Start(Target.Path));
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;

//COMアクセス
//IHTMLWindow2は操作できるが・・・
var iframeWindowCom = iframeWindow.DomWindow;
var sel = iframeWindowCom.document.selection;

//これは失敗する
//IHTMLSelectionObjectはGetType()でSystem.__ComObjectを返すので操作できない
var type = sel.type;

こんな時でも、OperationTypeInfoを使えば、目的の操作を実行することができます。

var type = sel.type(new OperationTypeInfo(typeof(IHTMLSelectionObject).FullName));

まあ、DLLインジェクションを使った方が早いですけどね

書き方難しいのは、普通に実装してDLLインジェクションした方がいいですね。その関数呼び出すだけなら簡単な書き方なので。

[TestMethod]
public void Test()
{
    var app = new WindowsAppFriend(Process.Start(Target.Path));
    WindowsAppExpander.LoadAssembly(app, GetType().Assembly);
    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);
    }

    //面倒な処理は相手プロセスに挿入して実行すればよい
    app.Type(GetType()).Func(web);
}

static void Func(WebBrowser web)
{
    var iframeWindow = web.Document.Window;
    var iframeWindowCom = (IHTMLWindow2)iframeWindow.DomWindow;
    var sel = iframeWindowCom.document.selection;
    var type = sel.type;
}

次回へ続く

なんで今さらこんなマニアックな話を出したかというと、実は次回へのフリだったのです。
と言うわけで次回へ続く・・・