概要‎ > ‎

モーダルダイアログ対応

*ここに記述されているサンプルはこちらからダウンロードできます。



Frinedlyではウィンドウ操作の基本クラスとして、WindowControlを提供しています。
このクラスは以下のことを目的としています。
・ウィンドウの取得。
・基本ウィンドウの操作。(.Netのコントロールの場合はFriendlyOperationも使えます。)

詳細な使い方は、APIリファレンスを参照してください。

最も重要なのはウィンドウの取得です。
安定したテストを書くためには確実はWindow取得方法が必須だからです。
しかし、最適な取得方法は画面によって異なります。
こちらにまとめましたので参照お願いします。

それからウィンドウ特定を補助するツールも提供しています。
こちらも使ってみてください。

また、操作も重要なのですが、実際にウィンドウを操作する場合は、このインターフェイスより
それぞれのコントロールに特化したインターフェイスの方が使いやすいと思います。
それぞれのコントロールに特化したクラスも提供しておりますので、そちらも合わせてご利用お願いします。
Codeer.Friendly.Windows.NativeStandardControls.dll
Ong.Friendly.FormsStandardControls.dll
RM.Friendly.WPFStandardControls.dll


ここでは、使い始めて、最初に困るポイントのモーダルダイアログの表示時のサンプルを紹介させていただきます。

プロダクトプロセスです。
ボタンを押すと、メッセージボックスが二回表示されます。

//プロダクトプロセス(操作対象)
using System;
using System.Windows.Forms;
 
namespace GuiOperationTarget
{
    public partial class SampleForm : Form
    {
        Button button;
        public SampleForm()
        {
            InitializeComponent();
        
            //ボタンを表示
            button = new Button();
            Controls.Add(button);
            button.Click += button_Click;
        }
 
        //クリックするとメッセージボックスを二回表示します。
        void button_Click(object sender, EventArgs e)
        {
            MessageBox.Show("1");
            MessageBox.Show("2");
        }
    }
}


テストコードです。
ボタンを押して、モーダルダイアログを表示して、それを閉じます。

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Diagnostics;
using Codeer.Friendly.Dynamic;
using Codeer.Friendly.Windows;
using Codeer.Friendly.Windows.Grasp;
using Codeer.Friendly;

namespace TestProcess
{
    [
TestClass]
   
public class GuiOperationSample
    {
       
WindowsAppFriend _app;
       
Process _process;

        [
TestInitialize]
       
public void TestInitialize()
        {
            _app =
new WindowsAppFriend(Process.Start("GuiOperationTarget.exe"));
            _process =
Process.GetProcessById(_app.ProcessId);
        }

        [
TestCleanup]
       
public void TestCleanup()
        {
            _app.Dispose();
            _process.CloseMainWindow();
        }

       
/// <summary>
       
/// モーダルダイアログ対応のサンプル
       
/// </summary>
        [
TestMethod]
       
public void TestModal()
        {
           
//最前面ウィンドウを取得。
           
WindowControl sampleForm = WindowControl.FromZTop(_app);

           
//ボタンを取得。
           
dynamic button = sampleForm.AppVar.Dynamic().button;

           
//クリック。モーダルウィンドウが出るため、非同期で実行する。
           
Async async = new Async();
            button.PerformClick(async);

           
//メッセージボックスは2回表示される
           
for (int i = 0; i < 2; i++)
            {
               
//sampleForm以外のウィンドウがモーダルになるのを待つ。
               
WindowControl messageBox = sampleForm.WaitForNextModal();

               
//メッセージボックスはネイティブウィンドウ。
               
//テキストからOKボタンを特定する。
               
WindowControl okButton = messageBox.IdentifyFromWindowText("OK");

               
//ボタンクリックを送信。
               
//NativeStandardControlsを使えば、もっと簡単に書くことができます。
               
const int BM_CLICK = 0x00F5;
                okButton.SendMessage(BM_CLICK,
IntPtr.Zero, IntPtr.Zero);

               
//メッセージボックスが破棄されるのを待つ。
                messageBox.WaitForDestroy();
            }

           
//PerformClickの処理が終了するのを待つ
            async.WaitForCompletion();
        }
    }
}


ここでポイントはWaitForNextModal、WaitForDestroy、WaitForCompletionです。
(*注意 WaitForNextModalはV1.6からのメソッドです。)

Friendlyでのプロダクトプロセスの操作呼び出しは基本は同期で実行されます。
これはタイミング依存で失敗することを防ぐためです。
そのため、モーダルダイアログが表示される処理を呼び出すと、そこで処理が止まってしまいます。
これを回避するためには、Asyncクラスを使用します。

しかし、非同期なので、ここでプロダクトプロセスの状態がわからなくなってしまいます。
次にメッセージボックスが表示されることはわかっているので、その状態になるまで待たなければなりません。
ここで安易にSleepを入れてしまうと、タイミング依存で失敗する可能性が入ってしまいます。

そこで、WaitForNextModalを使うことによって、確実に次のモーダルウィンドウが表示されるまで待ち合わせることができます。

また、場合によれば、ウィンドウの終了に関しても同期をとる必要があります。
今回は一回のイベント処理で二回メッセージボックスが表示されます。
もう一度WaitForNextModalを呼び出した場合、タイミングによっては、前のメッセージボックスが取得されることがあるのです。
そのため、確実に前のメッセージボックスが終了するのを待ちます。
 
最後にasync.WaitForCompletionを呼び出します。
期待通りの処理であれば、この処理は確実に終了するため、それを明示します。
(テストなので期待の動作は事前にわかるはずです。)

GUI操作のコードを書いていると、ここで上げた例以外にも何らかの処理の待ち合わせが発生するかもしれません。
その場合でも、極力確実な方法で待ち合わせ、タイミング依存のない方法を探してみてください。
WindowControlには、この他にも確実にウィンドウ処理を実施するためのメソッドが定義されているので参照お願いします。

通常のコントロールの操作に関しましては、メソッド、プロパティー、フィールドをすべて呼び出せますので、十分に操作できると思います。
また、GUI操作に関しましては、マイクロソフトのUIオートメーションなどもありますので、そちらの方が使いやすいと感じられる方は併用してみてください。

さて、最初の方で触れましたが、ウィンドウ特定に関しましては、ツールを使っていただくと簡単です。
次項ではツールの使い方を説明します。