次の方法で共有


PDC 2008

UI オートメーション クライアント アプリケーションの作成

更新日: 2009 年 4 月 28 日


ダウンロード

PDC08_Creating_UI_Automation_Client_Applications_JPN.docx (Word 形式、72 KB)


目次:

  1. はじめに
  2. UI オートメーション クライアント API の基礎
    1. CUIAutomation オブジェクトを作成する
    2. UI オートメーション要素を直接取得する
    3. UI オートメーションのクライアント向けプロパティ
    4. コントロール パターン
    5. コントロールの型
    6. 検索とナビゲーション
    7. 要素、プロパティ、およびパターンをプリフェッチしてパフォーマンスを向上する
    8. UI オートメーションのクライアント向けイベント
  1. 設計方法と考慮事項
    1. 自動テストに UI オートメーションを使用する
    2. UI オートメーションと画面の倍率変換
    3. UI オートメーションのスレッド処理の問題点
    4. Windows Vista と Windows 7 による UI オートメーション クライアントへのセキュリティ上の影響
  2. 詳細情報



Matt Karr
Microsoft Corporation
2008 年 9 月

対象:

Microsoft® Windows® オペレーティング システム

要約:

ユーザー補助や自動テストを目的として、他のアプリケーションのユーザー インターフェイス (UI) を調べて相互作用するために Microsoft UI オートメーションを使用する際の基本事項について説明します。

法的通知:

このドキュメントは暫定版であり、このソフトウェアの最終的な製品版の発売時に実質的に変更されることがあります。
このドキュメントに記載されている情報は、このドキュメントの発行時点におけるマイクロソフトの見解を反映したものです。変化する市場状況に対応する必要があるため、このドキュメントは、記載された内容の実現に関するマイクロソフトの確約とはみなされないものとします。また、発行以降に発表される情報の正確性に関して、マイクロソフトはいかなる保証もいたしません。
このホワイト ペーパーに記載された内容は情報の提供のみを目的としており、明示、黙示または法律の規定にかかわらず、これらの情報についてマイクロソフトはいかなる責任も負わないものとします。
お客様ご自身の責任において、 適用されるすべての著作権関連法規に従ったご使用を願います。このドキュメントのいかなる部分も、米国 Microsoft Corporation の書面による許諾を受けることなく、その目的を問わず、どのような形態であっても、複製または譲渡することは禁じられています。ここでいう形態とは、複写や記録など、電子的な、または物理的なすべての手段を含みます。ただしこれは、著作権法上のお客様の権利を制限するものではありません。
マイクロソフトは、このドキュメントに記載されている内容に関し、特許、特許申請、商標、著作権、またはその他の無体財産権を有する場合があります。別途マイクロソフトのライセンス契約上に明示の規定のない限り、このドキュメントはこれらの特許、商標、著作権、またはその他の無体財産権をお客様に許諾するものではありません。
© 2008 Microsoft Corporation. All rights reserved.
Microsoft、MS-DOS、Windows、Windows NT、Windows Server、Windows Vista、Active Directory、ActiveSync、ActiveX、Direct3D、DirectDraw、DirectInput、DirectMusic、DirectPlay、DirectShow、DirectSound、DirectX、Expression、FrontPage、HighMAT、Internet Explorer、JScript、Microsoft Press、MSN、Outlook、PowerPoint、SideShow、Silverlight、Visual Basic、Visual C++、Visual InterDev、Visual J++、Visual Studio、WebTV、Windows Media、Win32、Win32s、および Zune は米国 Microsoft Corporation の米国またはその他の国における登録商標または商標です。
記載されている会社名、製品名には、各社の商標のものもあります。


1. はじめに

アプリケーションから、別のアプリケーションのユーザー インターフェイス (UI) との相互作用が必要になることがあります。1 つ目のアプリケーションとしては、対象のアプリケーションの UI を自動テストするテスト アプリケーションが考えられます。また、目が不自由なユーザーの補助として、UI を読み上げるアプリケーションや、ユーザーが音声でコマンドを発行できる音声アプリケーションも考えられます。いずれの場合でも、こうしたアプリケーションには、システムやその他実行中のアプリケーションの UI を調査して相互作用する方法が必要です。

Microsoft® UI オートメーションは、まさにこれを行うためのツールです。このツールでは、UI を抽象化したモデルが提供されるため、クライアント アプリケーションでは、システム上で実行中のすべてのアプリケーションの UI を調査および操作することができます。

ページのトップへ


2. UI オートメーション クライアント API の基礎

Windows® 7 オペレーティング システムでは、UI オートメーションに、アンマネージ クライアント アプリケーションとマネージ クライアント アプリケーションの両方で使用できる、新しい COM クライアント API が含まれています。この資料では説明を簡単にするために、クライアント アプリケーションを "クライアント" と呼びます。アンマネージ クライアントでは、言語の変更や共通言語ランタイム (CLR) の読み込みを行う必要なく、UI オートメーションを使用できるようになると同時に、最新の機能からのメリットを得ることができます。新しい API は、以前のマネージ API と似ていますが、C++ 開発者にとっては以前よりも使いやすくなっています。

おそらく、UI オートメーション全般を理解し、COM クライアント API を具体的に理解するための最善の方法は、実際に動作しているところを見ることでしょう。次の短い例では、現在のカーソル位置にある UI 要素を取得し、その名前とコントロール型を出力するプログラム全体を示します。

int _tmain(int argc, _TCHAR* argv[])
{
    // COM を初期化して、主な Automation オブジェクトを作成します。
    IUIAutomation *g_pAutomation;
    CoInitialize(NULL);
    HRESULT hr = CoCreateInstance(__uuidof(CUIAutomation), NULL,
        CLSCTX_INPROC_SERVER, __uuidof(IUIAutomation), (void**)&g_pAutomation);
    if(FAILED(hr))
        return (hr);
    // カーソル位置にある要素を取得します。
    // GetPhysicalCursorPos を使用して、高 DPI でも正しく相互作用します。
    POINT pt;
    GetPhysicalCursorPos(&pt);
    IUIAutomationElement *pAtMouse;
    hr = g_pAutomation->ElementFromPoint(pt, &pAtMouse);
    if(FAILED(hr))
        return hr;
    
    // 要素の名前を取得して出力します。
    BSTR name;
    hr = pAtMouse->get_CurrentName(&name);
    if(SUCCEEDED(hr))
    {
        wprintf(L"Element's Name: %s \n", name);
        SysFreeString(name);
    }
    
    // 要素のコントロール型を (現在の言語で) 取得して、出力します。
    BSTR controlType;
    hr = pAtMouse->get_CurrentLocalizedControlType(&controlType);
    if(SUCCEEDED(hr))
    {
        wprintf(L"Element's Control Type: %s \n", controlType);
        SysFreeString(controlType);
    }
    
    // COM ポインターをクリーンアップします。
    pAtMouse->Release();
    g_pAutomation->Release();
    CoUninitialize();
    return 0;
}

では、この例を構成するさまざまな要素について説明していきましょう。

ページのトップへ

a. CUIAutomation オブジェクトを作成する

ここでは、IUIAutomation インターフェイスを実装するオブジェクトのインスタンスを作成することによって、UI オートメーション クライアント アプリケーションの作成に着手する方法について説明します。

新しい COM API の中心となるのは、IUIAutomation インターフェイスです。クライアントではこのインターフェイスを使用して、オートメーション要素の取得、イベント ハンドラーの登録、さまざまなヘルパー オブジェクトの作成、および他のヘルパー メソッドへのアクセスを行うことができます。

アプリケーションで UI オートメーションを使い始めるには、以下の手順を実行します。

  1. プロジェクトのヘッダーで Uiautomation.h をインクルードします。このヘッダーにより、API を定義する他のヘッダーが取り込まれます。
  2. IUIAutomation インターフェイスへのグローバル ポインターを宣言します。
  3. COM を初期化します。
  4. CUIAutomation クラスのインスタンスを作成し、グローバル ポインターに IUIAutomation インターフェイスを取得します。

次の例に示す関数は、オブジェクト インスタンスを作成し、取得したインターフェイス アドレスをグローバル ポインター g_pAutomation に格納します。

HRESULT InitializeUIAutomation()
{
    CoInitialize(NULL);
    HRESULT hr = CoCreateInstance(__uuidof(CUIAutomation), NULL,
        CLSCTX_INPROC_SERVER, __uuidof(IUIAutomation), (void**)&g_pAutomation);
    return (hr);
}

ページのトップへ

b. UI オートメーション要素を直接取得する

UI オートメーションのオブジェクトを作成したら、UI 全体を検出できます。UI は、オートメーション要素 (IUIAutomationElement インターフェイス オブジェクト) のツリーとしてモデル化され、それぞれの要素は (たとえば、ボタン、ウィンドウ、デスクトップといった) UI の 1 つを表します。IUIAutomationElement インターフェイスには、すべてのコントロールに関連するメソッド (プロパティの確認やフォーカスの設定など) が含まれます。

ルート要素

UI オートメーション ツリーのルート要素はデスクトップです。この要素は、IUIAutomation::GetRootElement メソッドまたは IUIAutomation::GetRootElementBuildCache メソッドのいずれかを呼び出すことで取得できます。どちらのメソッドも IUIAutomationElement インターフェイス ポインターを取得します。その後、IUIAutomationTreeWalker メソッドを呼び出してツリーを下位方向に移動したり、他のメソッド (IUIAutomationElement::FindFirstIUIAutomationElement::FindAll など) を呼び出して子孫要素を検索したりできます。

座標位置から

画面座標 (この例のカーソル位置など) がわかっている場合は、IUIAutomation::ElementFromPoint メソッドを呼び出して、IUIAutomationElement インターフェイスを取得できます。

ウィンドウ ハンドルから

ウィンドウ ハンドル (HWND) から IUIAutomationElement インターフェイスを取得するには、IUIAutomation::ElementFromHandle メソッドを呼び出します。

フォーカスが設定されたコントロールから

IUIAutomation::GetFocusedElement メソッドを呼び出すことで、フォーカスが設定されているコントロールを表す IUIAutomationElement インターフェイスを取得できます。

ページのトップへ

c. UI オートメーションのクライアント向けプロパティ

IUIAutomationElement オブジェクトのプロパティには、UI 要素 (通常はコントロール) に関する情報が含まれます。要素のプロパティはジェネリックで、コントロール型固有ではありません。要素のコントロール固有のプロパティは、そのコントロール パターンのインターフェイスによって公開されます。

UI オートメーション プロパティは読み取り専用です。コントロールのプロパティを設定するには、適切なコントロール パターンのメソッドを使用する必要があります。たとえば、スクロール ウィンドウの位置の値を変更する場合は、IScrollProvider::Scroll メソッドを使用します。

パフォーマンスを向上するために、要素の取得時に、コントロールおよびコントロール パターンのプロパティ値をキャッシュできます。

プロパティを取得する

一部ジェネリック プロパティとコントロール パターンのすべてのプロパティは、IUIAutomationElement インターフェイスまたはコントロール パターンのインターフェイスのプロパティとして使用でき、IUIAutomationElement::get_CurrentNameIUIAutomationDockPattern::get_CachedDockPosition などのアクセサー メソッドを使用して取得できます。

また、次に示すメソッドによって、現在のプロパティまたはキャッシュされたプロパティを取得できます。

  • IUIAutomationElement::GetCurrentPropertyValue
  • IUIAutomationElement::GetCurrentPropertyValueEx
  • IUIAutomationElement::GetCachedPropertyValue
  • IUIAutomationElement::GetCachedPropertyValueEx

これらのメソッドにより、あらゆるプロパティにアクセスできます。ただし、これらのメソッドでは値が VARIANT 構造体で返されるのに対し、個別のプロパティのアクセサーでは、返される値が適切な型にキャストされます。

プロパティとその詳細の完全な一覧については、「UI オートメーションの仕様と Community Promise」を参照してください。

プロパティ ID

ジェネリック アクセサー メソッドを使用するときは、UIAutomationClient.h で定義されたプロパティ ID を使用してプロパティを指定することも必要です。この ID は、プロパティ値の取得時、プロパティ条件の構築時、およびプロパティ変更イベントの購読時に、プロパティを指定するために使用します。

プロパティの既定値

UI オートメーション プロバイダーがプロパティを実装していない場合、UI オートメーション システムでは、そのプロパティの既定値を提供できます。たとえば、コントロールのプロバイダーが、UIA_HelpTextPropertyId というプロパティ ID で識別されるプロパティをサポートしていなければ、UI オートメーションは空文字列を返します。同様に、プロバイダーが UIA_IsDockPatternAvailablePropertyId で識別されるプロパティをサポートしていなければ、UI オートメーションは FALSE を返します。

IUIAutomationElement::GetCurrentPropertyValue メソッドと IUIAutomationElement::GetCurrentPropertyValueEx メソッドの違い (または同様のメソッド ペアの違い) は、後者のメソッドでは既定値が返されないように指定できる点にあります。既定値が返されない場合の戻り値は、プロパティがサポートされていないことを示す特殊な一意の定数になります。この値を受け取るときに、アプリケーションでは独自の値を提供するか、単純にプロパティを無視することができます。

ページのトップへ

d. コントロール パターン

コントロール パターンは、プロパティを補完するものです。コントロール パターンとは、UI 要素を記述し、UI 要素にアクセスできるようにする、関連するプロパティ、イベント、およびメソッドのコレクションです。1 つの要素に、複数のパターンを適用できます。1 つのコントロール パターンは、グループにまとめられた機能を表します。パターンには、Invoke パターンのように単純なものもあります。このパターンでは、クライアントからコントロールを呼び出すことができる 1 つのメソッドがサポートされます。一方、より複雑な Value パターンでは、コントロール値の取得と設定、値が読み取り専用かどうかのチェックなどがサポートされます。

コントロール パターンと、そのプロパティおよびメソッドの完全な一覧については、「UI オートメーションの仕様と Community Promise」を参照してください。

コントロール パターンを使用する

コントロール パターンは、IUIAutomationElement::GetCurrentPattern メソッドまたは IUIAutomationElement::GetCurrentPatternAs メソッド、あるいはこれらのメソッドのキャッシュされたバージョンを呼び出すことで取得できます。コントロール パターンのインターフェイスを取得したら、コントロール パターンのメソッドを直接呼び出すか、コントロール パターンのプロパティにアクセスすることによって、要素そのものと同じように使用します。

次の例では、Invoke パターンを単純に適用し、その唯一のメソッド (Invoke) を呼び出して、ボタンをクリックするか、ハイパーリンク先に移動します。

HRESULT InvokeElement(IUIAutomationElement *pElement)
{
    IUIAutomationInvokePattern * pInvoke;
    HRESULT hr = pElement->GetCurrentPatternAs(UIA_InvokePatternId,
                 __uuidof(IUIAutomationInvokePattern),
                 (void **)&pInvoke);
    if(FAILED(hr) || pInvoke == NULL)
    {
        return hr;
    }
    hr = pInvoke->Invoke();
    pInvoke->Release();
    return hr;
}

この例で IUIAutomationElement::GetCurrentPatternAs メソッドを使用しているのは、このメソッドでは QueryInterface メソッドが使用されるためです。

次のさらに複雑な例では、RangeValue パターンを使用して、コントロール (音量スライダーなど) を最大値に設定します。

HRESULT TurnItUp(IUIAutomationElement *pElement)
{
    IUIAutomationRangeValuePattern * pRangeVal;
    HRESULT hr = pElement->GetCurrentPatternAs(UIA_RangeValuePatternId,
                 __uuidof(IUIAutomationRangeValuePattern),
                 (void **)&pRangeVal);
    if(FAILED(hr) || pRangeVal == NULL)
    {
        return hr;
    }
    double max;
    hr = pRangeVal->get_CurrentMaximum(&max);
    if(SUCCEEDED(hr))
    {
        hr = pRangeVal->SetValue(max);
    }
    pRangeVal->Release();
    return hr;
}

上記の例ではどちらも、現在のパターンの取得時にエラーをチェックしているだけでなく、NULL もチェックしています。これは、NULL が、要求されたパターンをコントロールがサポートしていないときの有効な戻り値であるためです。

ページのトップへ

e. コントロールの型

コントロールの型は、構造上は単なる列挙プロパティに過ぎませんが、概念的なレベルでははるかに重要です。IUIAutomationElement::get_CurrentControlType メソッドや同様のメソッドによって取得されたときの要素のコントロール型は、ボタン、ウィンドウ、リスト ボックスといった大まかな分類になります。各型には、特定の目的のコントロール パターンを含みます。例を以下に示します。

  • ボタン コントロールは Invoke パターンまたは Toggle パターンのいずれかをサポートします。
  • ハイパーリンクは Invoke パターンをサポートし、可能であれば Value パターンをサポートします。
  • ウィンドウは Window パターンおよび Transform パターンをサポートし、必要に応じて Dock パターンをサポートします。

一部のコントロールには、コントロールの機能に応じて、複数のコントロール パターンの条件付きサポートがあります。たとえば、メニュー項目コントロールには、コントロールの機能に応じて、Invoke、ExpandCollapse、Toggle、および SelectionItem の各コントロール パターンの条件付きサポートがあります。

コントロール型およびサポートされるパターンの完全な一覧については、「UI オートメーションの仕様と Community Promise」を参照してください。

ページのトップへ

f. 検索とナビゲーション

ここまで説明してきた直接的な方法以外にも、要素を取得する主な方法が 2 つあります。1 つ目は Find* メソッドを使用する方法です。この方法では、UI オートメーション コアによってプロセス間呼び出しが最小限に抑えられるため、検索時間が短縮されます。2 つ目はツリー ウォーカーを使用して UI ツリーをたどる方法です。

Find 系のメソッドを使用する

Find 系のメソッドでは、親要素、検索スコープ、および検索条件 (最も重要) の 3 つが必要です。条件は、CUIAutomation オブジェクトのさまざまな API を呼び出して作成します。単純なプロパティ条件を作成できます。また、それらの条件を And、Or、および Not の各演算子と組み合わせて、より複雑な条件を作成することもできます。

条件を作成したら、検索の開始要素と、検索スコープを指定する必要があります。FindFirst メソッドまたは FindAll メソッドを呼び出す要素が、検索の開始要素です。通常、検索スコープには、要素自体、要素の子、または要素の子孫の 3 つのうち、1 つを選択します。一般に、検索スコープはできるだけ限定して、パフォーマンスを向上し、検索対象ではない要素が検出される機会を減らします。

次の検索例では、FindFirst メソッドを使用して、名前付きのアプリケーション ウィンドウを検索しています。この例では、検索条件 (特定の名前) が非常に単純です。もちろん、条件を大幅に複雑にすることもできます。

IUIAutomationElement* GetTopLevelWindowByName(LPWSTR windowName)
{
    if (!windowName)
    {
        return NULL;
    }
    IUIAutomationElement* pRoot;
    IUIAutomationElement* pFound;
    VARIANT varProp;
    varProp.vt = VT_BSTR;
    varProp.bstrVal = SysAllocString(windowName);
    // デスクトップ要素を取得します。
    HRESULT hr = g_pAutomation->GetRootElement(&pRoot);
    // トップレベル要素を ("Program Manager" などの) 名前で取得します。
    if (pRoot)
    {
        IUIAutomationCondition* pCondition;
        g_pAutomation->CreatePropertyCondition(UIA_NamePropertyId, varProp, 
            &pCondition);
        pRoot->FindFirst(TreeScope_Children, pCondition, &pFound);
        pRoot->Release();
        pCondition->Release();
    }
    VariantClear(&varProp);
    return pFound;
}

次の例では、ボタン以外の呼び出し可能なコントロールをすべて検出するという、複雑な条件を示します。

IUIAutomationCondition * GetInvokableNonButtonCondition()
{
    IUIAutomationCondition* pInvokableCondition;
    IUIAutomationCondition* pButtonCondition;
    IUIAutomationCondition* pNotButtonCondition;
    IUIAutomationCondition* pInvokableNotButtonCondition;
    // ボタン コントロール型のプロパティ条件を作成します。
    VARIANT varProp;
    varProp.vt = VT_I4;
    varProp.lVal = UIA_ButtonControlTypeId;
    g_pAutomation->CreatePropertyCondition(UIA_ControlTypePropertyId, varProp,
        &pButtonCondition);
    // InvokePattern をチェックするプロパティ条件を作成します。
    varProp.vt = VT_BOOL;
    varProp.boolVal = VARIANT_TRUE;
    g_pAutomation->CreatePropertyCondition(UIA_IsInvokePatternAvailablePropertyId,
        varProp, &pInvokableCondition);
    // ボタンに対する Not 条件を作成します。
    g_pAutomation->CreateNotCondition(pButtonCondition, &pNotButtonCondition);
    // 条件を組み合わせます。
    g_pAutomation->CreateAndCondition(pNotButtonCondition, pInvokableCondition,
        (&pInvokableNotButtonCondition);
    pInvokableCondition->Release();
    pButtonCondition->Release();
    pNotButtonCondition->Release();
    return pInvokableNotButtonCondition;
}

次のような理由から、FindFirst メソッドと FindAll メソッドのどちらを選択するかも重要になります。

  • FindFirst では、検索で最初に一致する要素が返されます。これは、要素を 1 つだけ検索する (またはその必要がある) 場合に使用します。
  • FindAll では、スコープ内で一致するすべての要素が検索されるまで続行されます。これは、一致する要素のコレクションが必要な場合に使用します。

注: 一般に、ルート要素から検索を開始する場合は、検索のスコープが子要素のみに限定されます。子孫やサブツリーを検索すると、要素の検索が数百回または数千回も繰り返される場合があるため、スタック オーバーフローが発生する可能性があります。下位レベルにある特定の要素を取得する場合は、アプリケーション ウィンドウまたは下位レベルのコンテナーから検索を開始します。

IUIAutomationTreeWalkers を使用する

ツリー全体をナビゲートするもう 1 つの方法は、ツリー ウォーカーを使用することです。ツリー ウォーカーでは、親、子、および兄弟ノードを移動するような、直接的なナビゲーション メソッドを使用して、ツリーのフィルター ビューをスキャンできます。組み込みのフィルター ビューには、次のようなものがあります。

  • 未加工 (フィルターなし): すべての要素が表示されます。
  • コントロール ビュー (既定値): 冗長な要素、またはレイアウト目的のみで使用される要素を除外します。
  • コンテンツ ビュー: コントロール ビューよりもさらに選択的にコントロールをフィルター選択します。

ユーザーは、条件付きの独自のカスタム ビューを作成することもできます。

ツリーのスキャンは、探索アプリケーションによって行われます。また、特定要素の検出が困難でも、その要素に既知の安定したリレーションシップ (子や親など) が比較的見つかりやすい場合にも使用されます。

次の簡単な例では、pElement によって参照されるコントロールの最初の子要素までたどる方法を示します。

// コントロール ビュー ウォーカーを取得します。
IUIAutomationTreeWalker * pWalk;  
g_pAutomation->get_ControlViewWalker(&pWalk);
// 要素の最初の子要素に移動します。
IUIAutomationElement * pFirst;
pWalk->GetFirstChildElement(pElement, &pFirst);

ページのトップへ

g. 要素、プロパティ、およびパターンをプリフェッチしてパフォーマンスを向上する

その性質上、各プロセス間呼び出しで CPU 時間が使用されるため、他のプロセスに含まれる UI との通信は低速になる傾向があります。(場合によって、多数の要素の) 多数のプロパティが必要な場合には、1 回のプロセス間呼び出しによって複数のプロパティを一度に要求することにより、CPU 使用率の総計を削減できます。

UI オートメーションでは、プリフェッチによってこの最適化が可能になります。プリフェッチは、まずデータのキャッシュを要求してから、キャッシュ関連のメソッドを呼び出してデータを格納および取得することによって実行されます。UI オートメーション要素を返すほとんどのメソッドには、IUIAutomation::GetFocusedElement メソッドと IUIAutomation::GetFocusedElementBuildCache メソッドのペアで行われるような、キャッシュの構築に相当するものもあります。

キャッシュを要求する

キャッシュを要求するのは簡単です。CUIAutoamation::CreateCacheRequest メソッドを呼び出して空のキャッシュを取得してから、プロパティおよびパターンをこのキャッシュに追加します。必要に応じて、スコープやフィルター処理したツリーを追加して、要素のサブツリーの一部またはすべてをフェッチすることもできます。

キャッシュされているデータを取得する

キャッシュを設定したら、キャッシュを構築するメソッドの 1 つを呼び出すか、IUIAutomationElement::BuildUpdatedCache メソッドを呼び出せば、キャッシュされたプロパティ、パターン、および (場合によっては) 子要素全体が含まれているオートメーション要素が提供されます。このデータにはほぼ通常どおりにアクセスできます。ただし、IUIAutomationElement::get_CachedNameIUIAutomationElement::GetCachedPropertyValueIUIAutomationElement::GetCachedPattern など、キャッシュ バージョンのメソッドを使用します。子要素またはサブツリーをキャッシュしている場合は、操作に大きな違いがあります。つまり、Find 系のメソッドやツリー ウォーカーを使用してナビゲートするのではなく、IUIAutomationElement::GetCachedParent メソッドおよび IUIAutomationElement::GetCachedChildren メソッドを使用します。

次の関数例では、キャッシュのさまざまな機能を示しています。この関数は、SelectionItem パターンをサポートする特定の要素の子要素すべての、Name パターンと SelectionItem パターンをプリフェッチします。その後、子要素全体をスキャンして、それぞれのキャッシュされた Name をチェックします。"T" という文字で始まる名前を持つすべての子要素を、Selection に追加します (この例のデザインは、リスト ボックスまたはリスト ビュー コントロールに最も適しています)。

HRESULT SelectT(IUIAutomationElement *pElement)
{
    IUIAutomationCacheRequest * pCacheReq;
    g_pAutomation->CreateCacheRequest(&pCacheReq);
    pCacheReq->AddProperty(UIA_NamePropertyId);
    pCacheReq->AddPattern(UIA_SelectionItemPatternId);
    pCacheReq->put_TreeScope(TreeScope_Children);
    // SelectionItem パターンをチェックするプロパティ条件を作成します。
    VARIANT varProp;
    varProp.vt = VT_BOOL;
    varProp.boolVal = VARIANT_TRUE;
    IUIAutomationCondition* pSelectableCondition;
    g_pAutomation->CreatePropertyCondition(
        UIA_IsSelectionItemPatternAvailablePropertyId,
        varProp, &pSelectableCondition);
    // そのプロパティをツリー フィルターとして設定します。
    pCacheReq->put_TreeFilter(pSelectableCondition);
    pSelectableCondition->Release();
    IUIAutomationElement * pCachedElement;
    HRESULT hr = pElement->BuildUpdatedCache(pCacheReq, &pCachedElement);
    if(FAILED(hr))
        return hr;
    pCacheReq->Release();
    // 先ほどプリフェッチした子要素を取得します。
    IUIAutomationElementArray *pCachedChildren;
    hr = pCachedElement->GetCachedChildren(&pCachedChildren);
    if(FAILED(hr) || pCachedChildren == NULL)
        return hr;
    int length;
    hr = pCachedChildren->get_Length(&length);
    if(FAILED(hr))
        return hr;
    // 子要素の配列全体を順番に処理します。
    for(int i = 0; i < length; i++)
    {
        IUIAutomationElement * pCachedChild;
        hr = pCachedChildren->GetElement(i, &pCachedChild);
        if(FAILED(hr) || pCachedChild == NULL)
            return hr;
        BSTR name;
        hr = pCachedChild->get_CachedName(&name);
        if(FAILED(hr))
            return hr;
        // 要素の名前が "T" で始まるものを選択します。
        if(name[0] == L'T')
        {
            IUIAutomationSelectionItemPattern * pSelectionItem;
            HRESULT hr = pCachedChild->GetCachedPatternAs(
                UIA_SelectionItemPatternId,
                __uuidof(IUIAutomationSelectionItemPattern),
                (void **)&pSelectionItem);
            if(SUCCEEDED(hr) && pSelectionItem != NULL)
            {
                pSelectionItem->AddToSelection();
                pSelectionItem->Release();
            }
        }
        
        SysFreeString(name);
        pCachedChild->Release();
    }
    pCachedChildren->Release();
    pCachedElement->Release();
    return S_OK;
}

ページのトップへ

h. UI オートメーションのクライアント向けイベント

UI オートメーションでは、クライアントから目的とするイベントを購読できます。この機能により、システム内のすべての UI 要素を連続的にポーリングして、情報、構造、状態が変化していないかを確認する必要がなくなるため、パフォーマンスが向上します。

また、定義済みスコープ内のイベントのみをリッスンするため、効率も向上します。たとえば、クライアントは、ツリー内のすべての UI オートメーション要素のフォーカス変更イベント、または 1 つだけの要素とその子孫要素のフォーカス変更イベントをリッスンできます。

: 可能性のあるイベントがすべて Microsoft UI オートメーション プロバイダーから発生するとは想定しないでください。たとえば、プロパティの変化すべてが、Windows フォーム コントロールや Win32 コントロールの標準プロキシ プロバイダーからイベントとして発生するとは限りません。

イベントを購読する

クライアント アプリケーションは、AddAutomationEventHandlerAddFocusChangedEventHandler などのメソッドを使用してイベント ハンドラーに登録することによって、特定の種類のイベントを購読します。これらのメソッドでは、コールバック インターフェイスを受け取り、クライアントによって作成されるオブジェクトに実装します。その後、IUIAutomationEventHandler::HandleEvent などのメソッドが、イベントの発生時に適切なスレッドで呼び出されます。

シャットダウン時、または UI オートメーション イベントがアプリケーションの購読対象ではなくなったとき、UI オートメーション クライアントがイベントを削除します。クライアントは、RemoveAutomationEventHandlerRemoveFocusChangedEventHandler などの具体的な削除用メソッドを呼び出すか、すべてのイベントを削除する RemoveAllEventHandlers メソッドを呼び出します。

次のコード例では、IUIAutomationFocusChangedEventHandler インターフェイスを実装するクラスの宣言を示します。

class CFocusHandler : IUIAutomationFocusChangedEventHandler
{
public:
    CFocusHandler(void);
    ~CFocusHandler(void);
    // IUnknown メソッド
    ULONG STDMETHODCALLTYPE AddRef();
    ULONG STDMETHODCALLTYPE Release();
    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void**ppInterface);
    // IUIAutomationFocusChangedEventHandler メソッド
    HRESULT STDMETHODCALLTYPE HandleFocusChangedEvent(IUIAutomationElement *sender);
    DWORD _refCount;
};

オブジェクトの IUIAutomationFocusChangedEventHandler::HandleFocusChangedEvent メソッド実装では、次の呼び出しが行われた後のすべてのフォーカス変更イベントが通知されます。

HRESULT AddFocusHandler(IUIAutomation* pAutomation)
{ 
    // CFocusHandler は次のインターフェイスを実装するクラスです。
    // IUIAutomationFocusChangedEventHandler
    CFocusHandler* pFocusHandler = new CFocusHandler();
    if (!pFocusHandler)
    {
        return E_OUTOFMEMORY;
    }
    IUIAutomationFocusChangedEventHandler* pHandler;
    pFocusHandler->QueryInterface(IID_IUIAutomationFocusChangedEventHandler, (void**)&pHandler);
    HRESULT hr = pAutomation->AddFocusChangedEventHandler(NULL, pHandler);
    pFocusHandler->Release();
    return hr;
}

ページのトップへ


3. 設計方法と考慮事項

ここでは、クライアント アプリケーション向けに UI オートメーションを使用する際の設計上の考慮事項について説明します。自動テスト用アプリケーション、画面の拡大縮小、およびセキュリティ上の問題に関しては、特に注意を払う必要があります。

a. 自動テストに UI オートメーションを使用する

多くの自働テスト ツールやシナリオの目標は、ユーザー インターフェイスに対して一貫性があり、反復可能な操作を行うことです。このような操作には、特定のコントロールの単純な単体テストから、コントロールのグループに対する一連の一般的な操作を反復処理する、テスト スクリプトの記録と再生を組み合わせた複雑なテストまで含みます。

自動アプリケーションの 1 つの問題点は、動的な対象にテストを同期するのが困難な点です。たとえば、Windows タスク マネージャに含まれているような、現在実行中のアプリケーションを一覧表示するリスト ボックス コントロールを考えてみましょう。リスト ボックス内の項目はテスト アプリケーションの制御が及ぶ範囲外で動的に更新されるため、リスト ボックスの特定の項目を一貫性を保って繰り返し選択することは不可能です。テスト アプリケーションの制御が及ばない UI のフォーカス切り替えを単純に繰り返そうとする場合でも、同様の問題が起きます。

プログラムからのアクセス

プログラムによるアクセスでは、従来のマウス入力やキーボード入力によって行われる相互作用や操作性を、コードで模倣する機能が提供されます。UI オートメーションは、次の 5 つのコンポーネントによりプログラムからのアクセスを可能にします。

  • UI オートメーション ツリーは、UI 構造全体のナビゲーションを容易にします。ツリーは、ウィンドウ ハンドルのコレクションから構築されます。詳細については、https://msdn.microsoft.com/ja-jp/library/ms741931(VS.80).aspx を参照してください。
  • オートメーションの各要素が、UI の個別のコンポーネントです。これらの要素は、多くの場合、ウィンドウ ハンドルよりも細かくなります。
  • オートメーションのプロパティが、UI 要素に関する具体的な情報を提供します。詳細については、https://msdn.microsoft.com/ja-jp/library/ms752056.aspx を参照してください。
  • コントロールのパターンが、コントロールの機能の特定の側面を定義します。パターンは、プロパティ、メソッド、イベント、および構造体に関する情報から構成することができます。詳細については、https://msdn.microsoft.com/ja-jp/library/ms752362.aspx を参照してください。
  • オートメーションのイベントが、イベント通知と情報を提供します。詳細については、https://msdn.microsoft.com/ja-jp/library/ms748252(VS.80).aspx を参照してください。

自動テスト向けの主なプロパティ

UI 内の任意のコントロールを一意に識別して検索する機能は、自動テスト アプリケーションがその UI を処理する基盤となります。クライアントとプロバイダーによって使用されるいくつかの UI オートメーション プロパティが、この識別と検索を支援します。

プロパティ 説明
AutomationId オートメーション要素をその兄弟要素から一意に識別します。製品が複数言語で出荷される場合に通常ローカライズされる Name などのプロパティとは異なり、AutomationID プロパティはローカライズされません。
ControlType オートメーション要素によって表されるコントロール型を識別します。コントロール型がわかると、そこから多くの情報を推測できます。詳細については、https://msdn.microsoft.com/ja-jp/library/ms743581(VS.80).aspx を参照してください。
Name コントロールを識別または説明するテキスト文字列です。ローカライズされる可能性があり、またアプリケーションの状態や状況に応じて変更される場合があるため、このプロパティ値は注意して使用する必要があります。実際に、Name プロパティは兄弟間でさえ一意にならない場合があります。UI の表示に一貫性がある限り、ピア間で同じ Name 値を使用することができます。自動テストの場合、クライアントでは AutomationId プロパティまたは RuntimeId プロパティの使用を検討してください。

注: AutomationId プロパティでは、オートメーション ツリー全体が一意に識別されるとは限りません。たとえば、アプリケーションには、複数のトップレベルのメニュー項目を持つメニュー コントロールが含まれ、さらに、それらのメニュー項目に複数の子メニュー項目が含まれている場合があります。これらの二次的なメニュー項目は、Item1Item2Item3 といった汎用的な手法で識別されることがあり、トップレベルのメニュー項目の間では、子項目の ID が重複してもかまいません。

ページのトップへ

b. UI オートメーションと画面の倍率変換

Windows Vista® オペレーティング システムからは、ユーザーがドット/インチ (dpi) 単位で画面の解像度を変更して、ほとんどの UI 要素を拡大表示することができます。この機能は Microsoft® Windows® クライアント オペレーティング システムでは以前から利用できていましたが、倍率変換をアプリケーションが実装している必要がありました。現在は、アプリケーションで独自の倍率変換を処理していなくても、デスクトップ ウィンドウ マネージャーが既定の倍率変換を実行します。UI オートメーション クライアント アプリケーションでは、この機能を考慮に入れる必要があります。

UI オートメーション クライアントで倍率を変換する

UI オートメーション では論理座標を使用しません。次のメソッドとプロパティは、物理座標を返すか、物理座標をパラメーターとして受け取ります。

  • IUIAutomation::ElementFromPoint
  • IUIAutomation::ElementFromPointBuildCache
  • IUIAutomationElement::GetClickablePoint
  • IUIAutomationElement::CurrentBoundingRectangle
  • IUIAutomationElement::CachedBoundingRectangle
  • IRawElementProviderFragment::BoundingRectangle

既定では、96 dpi 以外の画面解像度で実行されている UI オートメーション アプリケーションは、これらのメソッドとプロパティから正しい結果を取得できません。たとえば、カーソル位置は論理座標で表現されるため、クライアントはこれらの座標を単に IUIAutomation::ElementFromPoint メソッドに渡すだけでは、カーソルの下にある要素を取得できません。また、アプリケーションは、クライアント領域外にはウィンドウを正しく配置できません。

この解決方法は次の 2 つの部分から構成されます。

  • 起動時に Win32 関数の SetProcessDPIAware を呼び出して、クライアント アプリケーションに画面の解像度を認識させます。この関数は、プロセス全体に画面解像度を認識させます。そのため、プロセスに属しているすべてのウィンドウは倍率変換されません。
  • GetPhysicalCursorPos 関数を呼び出して、現在のカーソルの座標を取得します。

ページのトップへ

c. UI オートメーションのスレッド処理の問題点

クライアント アプリケーションが UI スレッドで独自の UI と相互作用しようとすると、UI オートメーションによる Windows メッセージの使用方法が原因で競合が発生する可能性があります。こうした競合が生じると、パフォーマンスが著しく低下したり、アプリケーションが応答しなくなることさえあります。

クライアント アプリケーションが、独自の UI を含め、デスクトップ上のすべての要素との相互作用を意図している場合は、UI オートメーションのすべての呼び出しを独立したスレッドで行う必要があります。この推奨事項は、アプリケーションからの (IUIAutomationTreeWalker インターフェイスや IUIAutomationElement::FindAll メソッドなどによる) 要素の検索やコントロール パターンの使用も該当します。

UI オートメーション イベント ハンドラーは UI スレッドで呼び出されることがないため、このイベント ハンドラー内での UI オートメーションの呼び出しは安全に行うことができます。ただし、クライアント アプリケーションの UI から発生する可能性があるイベントを購読する場合は、IUIAutomation::AddAutomationEventHandler (または関連メソッド) を UI スレッド以外で呼び出す必要があります。同じスレッド上のイベント ハンドラーは削除します。

ページのトップへ

d. Windows Vista と Windows 7 による UI オートメーション クライアントへのセキュリティ上の影響

ここでは、Windows Vista と Windows® 7 オペレーティング システム上で実行されている場合の、UI オートメーション クライアントへのセキュリティ上の影響について説明します。

ユーザー アカウント制御

Windows Vista で導入された革新的なセキュリティ機能の中に、標準ユーザー (管理者以外のユーザー) として実行していても、管理者権限を必要とするアプリケーションやサービスの実行が必ずしもブロックされない機能があります。

大部分のアプリケーションには、標準トークンまたは管理者トークンのいずれかが付与されます。アプリケーションを管理者アプリケーションとして識別できない場合、既定ではそのアプリケーションが標準アプリケーションとして起動されます。管理者アプリケーションとして識別されたアプリケーションが起動される場合、オペレーティング システムは、起動前に、権限を昇格してアプリケーションを実行するかどうかユーザーに同意を求めます。ユーザーがローカル管理者グループのメンバーであっても、同意を求めるメッセージが既定で表示されます。これは、実行に管理者資格が必要なアプリケーションやシステム コンポーネントが実行の許可を求めるまでは、管理者であっても、標準ユーザーとして実行されているためです。

高いユーザー権利が必要なタスク

管理者特権が必要なタスクを実行しようとすると、Windows Vista および Windows 7 では、続行するかどうかを確認するダイアログ ボックスが表示されます。このダイアログ ボックスはプロセス間通信から保護されているため、悪質なソフトウェアによってユーザー入力のシミュレーションが行われることはありません。同様に、通常、他のプロセスからデスクトップ ログオン画面にアクセスすることはできません。

UI オートメーション クライアントは、他のプロセスと通信する必要がありますが、一部のプロセスは高いレベルのユーザー権限で実行されている可能性があります。また、クライアントでは、通常は他のプロセスには表示されないシステム ダイアログ ボックスへのアクセスが必要になる場合があります。したがって、UI オートメーション クライアントはシステムによって信頼され、特別なユーザー権利で実行される必要があります。

高いレベルのユーザー権利で実行されているアプリケーションと通信できるように信頼されるには、アプリケーションに署名する必要があります。

マニフェスト ファイル

保護されたシステムの UI にアクセスするには、特別な属性を含むマニフェスト ファイルを備えたアプリケーションをビルドする必要があります。この UIAccess 属性は、次のように <requestedExecutionLevel> タグに含まれます。

<trustInfo xmlns="urn:0073chemas-microsoft-com:asm.v3"> 
&lt;security&gt; 

    &lt;requestedPrivileges&gt; 

    &lt;requestedExecutionLevel 

        level=&quot;highestAvailable&quot; 

        UIAccess=&quot;true&quot; /&gt; 

    &lt;/requestedPrivileges&gt; 

&lt;/security&gt; 

</trustInfo>

(このコードの level 属性の値は、単なる一例です)。

UIAccess 属性の値は、既定で "false" に設定されます。つまり、この属性を省略した場合、またはアセンブリのマニフェストが存在しない場合、アプリケーションは保護された UI にアクセスできません。

ページのトップへ


4. 詳細情報

  • 最新の Windows Automation API 3.0 の詳細およびライブラリ (UI オートメーションおよび Microsoft Active Accessibility を含む) については、今後の Windows® 7 オペレーティング システム用 Windows® ソフトウェア開発キット (SDK) および Microsoft® .NET Framework 3.5 Service Pack 1 を参照してください。
  • 最新の Windows Automation API 3.0 の詳細については、CoDe Focus Magazine の記事「Windows Automation API 3.0 Overview (英語)」を参照してください。
  • 一般的にアクセスできる製品開発リソースについては、マイクロソフトのアクセシビリティ デベロッパー センターを参照してください。
  • UI オートメーションの仕様については、「UI オートメーションの仕様と Community Promise」を参照してください。

Web アドレスは変更される可能性があるため、ここに記載されている Web サイトに接続できない場合があります。

ページのトップへ