UI オートメーション クライアント アプリケーションの作成更新日: 2009 年 4 月 28 日 ダウンロードPDC08_Creating_UI_Automation_Client_Applications_JPN.docx (Word 形式、72 KB) 目次:
Matt Karr 対象:Microsoft® Windows® オペレーティング システム 要約:ユーザー補助や自動テストを目的として、他のアプリケーションのユーザー インターフェイス (UI) を調べて相互作用するために Microsoft UI オートメーションを使用する際の基本事項について説明します。 法的通知:このドキュメントは暫定版であり、このソフトウェアの最終的な製品版の発売時に実質的に変更されることがあります。 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 要素を取得し、その名前とコントロール型を出力するプログラム全体を示します。
では、この例を構成するさまざまな要素について説明していきましょう。 ページのトップへ a. CUIAutomation オブジェクトを作成するここでは、IUIAutomation インターフェイスを実装するオブジェクトのインスタンスを作成することによって、UI オートメーション クライアント アプリケーションの作成に着手する方法について説明します。 新しい COM API の中心となるのは、IUIAutomation インターフェイスです。クライアントではこのインターフェイスを使用して、オートメーション要素の取得、イベント ハンドラーの登録、さまざまなヘルパー オブジェクトの作成、および他のヘルパー メソッドへのアクセスを行うことができます。 アプリケーションで UI オートメーションを使い始めるには、以下の手順を実行します。
次の例に示す関数は、オブジェクト インスタンスを作成し、取得したインターフェイス アドレスをグローバル ポインター g_pAutomation に格納します。
ページのトップへ b. UI オートメーション要素を直接取得するUI オートメーションのオブジェクトを作成したら、UI 全体を検出できます。UI は、オートメーション要素 (IUIAutomationElement インターフェイス オブジェクト) のツリーとしてモデル化され、それぞれの要素は (たとえば、ボタン、ウィンドウ、デスクトップといった) UI の 1 つを表します。IUIAutomationElement インターフェイスには、すべてのコントロールに関連するメソッド (プロパティの確認やフォーカスの設定など) が含まれます。 ルート要素UI オートメーション ツリーのルート要素はデスクトップです。この要素は、IUIAutomation::GetRootElement メソッドまたは IUIAutomation::GetRootElementBuildCache メソッドのいずれかを呼び出すことで取得できます。どちらのメソッドも IUIAutomationElement インターフェイス ポインターを取得します。その後、IUIAutomationTreeWalker メソッドを呼び出してツリーを下位方向に移動したり、他のメソッド (IUIAutomationElement::FindFirst や IUIAutomationElement::FindAll など) を呼び出して子孫要素を検索したりできます。 座標位置から画面座標 (この例のカーソル位置など) がわかっている場合は、IUIAutomation::ElementFromPoint メソッドを呼び出して、IUIAutomationElement インターフェイスを取得できます。 ウィンドウ ハンドルからウィンドウ ハンドル (HWND) から IUIAutomationElement インターフェイスを取得するには、IUIAutomation::ElementFromHandle メソッドを呼び出します。 フォーカスが設定されたコントロールからIUIAutomation::GetFocusedElement メソッドを呼び出すことで、フォーカスが設定されているコントロールを表す IUIAutomationElement インターフェイスを取得できます。 ページのトップへ c. UI オートメーションのクライアント向けプロパティIUIAutomationElement オブジェクトのプロパティには、UI 要素 (通常はコントロール) に関する情報が含まれます。要素のプロパティはジェネリックで、コントロール型固有ではありません。要素のコントロール固有のプロパティは、そのコントロール パターンのインターフェイスによって公開されます。 UI オートメーション プロパティは読み取り専用です。コントロールのプロパティを設定するには、適切なコントロール パターンのメソッドを使用する必要があります。たとえば、スクロール ウィンドウの位置の値を変更する場合は、IScrollProvider::Scroll メソッドを使用します。 パフォーマンスを向上するために、要素の取得時に、コントロールおよびコントロール パターンのプロパティ値をキャッシュできます。 プロパティを取得する一部ジェネリック プロパティとコントロール パターンのすべてのプロパティは、IUIAutomationElement インターフェイスまたはコントロール パターンのインターフェイスのプロパティとして使用でき、IUIAutomationElement::get_CurrentName や IUIAutomationDockPattern::get_CachedDockPosition などのアクセサー メソッドを使用して取得できます。 また、次に示すメソッドによって、現在のプロパティまたはキャッシュされたプロパティを取得できます。
これらのメソッドにより、あらゆるプロパティにアクセスできます。ただし、これらのメソッドでは値が 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) を呼び出して、ボタンをクリックするか、ハイパーリンク先に移動します。
この例で IUIAutomationElement::GetCurrentPatternAs メソッドを使用しているのは、このメソッドでは QueryInterface メソッドが使用されるためです。 次のさらに複雑な例では、RangeValue パターンを使用して、コントロール (音量スライダーなど) を最大値に設定します。
上記の例ではどちらも、現在のパターンの取得時にエラーをチェックしているだけでなく、NULL もチェックしています。これは、NULL が、要求されたパターンをコントロールがサポートしていないときの有効な戻り値であるためです。 ページのトップへ e. コントロールの型コントロールの型は、構造上は単なる列挙プロパティに過ぎませんが、概念的なレベルでははるかに重要です。IUIAutomationElement::get_CurrentControlType メソッドや同様のメソッドによって取得されたときの要素のコントロール型は、ボタン、ウィンドウ、リスト ボックスといった大まかな分類になります。各型には、特定の目的のコントロール パターンを含みます。例を以下に示します。
一部のコントロールには、コントロールの機能に応じて、複数のコントロール パターンの条件付きサポートがあります。たとえば、メニュー項目コントロールには、コントロールの機能に応じて、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 メソッドを使用して、名前付きのアプリケーション ウィンドウを検索しています。この例では、検索条件 (特定の名前) が非常に単純です。もちろん、条件を大幅に複雑にすることもできます。
次の例では、ボタン以外の呼び出し可能なコントロールをすべて検出するという、複雑な条件を示します。
次のような理由から、FindFirst メソッドと FindAll メソッドのどちらを選択するかも重要になります。
注: 一般に、ルート要素から検索を開始する場合は、検索のスコープが子要素のみに限定されます。子孫やサブツリーを検索すると、要素の検索が数百回または数千回も繰り返される場合があるため、スタック オーバーフローが発生する可能性があります。下位レベルにある特定の要素を取得する場合は、アプリケーション ウィンドウまたは下位レベルのコンテナーから検索を開始します。 IUIAutomationTreeWalkers を使用するツリー全体をナビゲートするもう 1 つの方法は、ツリー ウォーカーを使用することです。ツリー ウォーカーでは、親、子、および兄弟ノードを移動するような、直接的なナビゲーション メソッドを使用して、ツリーのフィルター ビューをスキャンできます。組み込みのフィルター ビューには、次のようなものがあります。
ユーザーは、条件付きの独自のカスタム ビューを作成することもできます。 ツリーのスキャンは、探索アプリケーションによって行われます。また、特定要素の検出が困難でも、その要素に既知の安定したリレーションシップ (子や親など) が比較的見つかりやすい場合にも使用されます。 次の簡単な例では、pElement によって参照されるコントロールの最初の子要素までたどる方法を示します。
ページのトップへ g. 要素、プロパティ、およびパターンをプリフェッチしてパフォーマンスを向上するその性質上、各プロセス間呼び出しで CPU 時間が使用されるため、他のプロセスに含まれる UI との通信は低速になる傾向があります。(場合によって、多数の要素の) 多数のプロパティが必要な場合には、1 回のプロセス間呼び出しによって複数のプロパティを一度に要求することにより、CPU 使用率の総計を削減できます。 UI オートメーションでは、プリフェッチによってこの最適化が可能になります。プリフェッチは、まずデータのキャッシュを要求してから、キャッシュ関連のメソッドを呼び出してデータを格納および取得することによって実行されます。UI オートメーション要素を返すほとんどのメソッドには、IUIAutomation::GetFocusedElement メソッドと IUIAutomation::GetFocusedElementBuildCache メソッドのペアで行われるような、キャッシュの構築に相当するものもあります。 キャッシュを要求するキャッシュを要求するのは簡単です。CUIAutoamation::CreateCacheRequest メソッドを呼び出して空のキャッシュを取得してから、プロパティおよびパターンをこのキャッシュに追加します。必要に応じて、スコープやフィルター処理したツリーを追加して、要素のサブツリーの一部またはすべてをフェッチすることもできます。 キャッシュされているデータを取得するキャッシュを設定したら、キャッシュを構築するメソッドの 1 つを呼び出すか、IUIAutomationElement::BuildUpdatedCache メソッドを呼び出せば、キャッシュされたプロパティ、パターン、および (場合によっては) 子要素全体が含まれているオートメーション要素が提供されます。このデータにはほぼ通常どおりにアクセスできます。ただし、IUIAutomationElement::get_CachedName、IUIAutomationElement::GetCachedPropertyValue、IUIAutomationElement::GetCachedPattern など、キャッシュ バージョンのメソッドを使用します。子要素またはサブツリーをキャッシュしている場合は、操作に大きな違いがあります。つまり、Find 系のメソッドやツリー ウォーカーを使用してナビゲートするのではなく、IUIAutomationElement::GetCachedParent メソッドおよび IUIAutomationElement::GetCachedChildren メソッドを使用します。 例次の関数例では、キャッシュのさまざまな機能を示しています。この関数は、SelectionItem パターンをサポートする特定の要素の子要素すべての、Name パターンと SelectionItem パターンをプリフェッチします。その後、子要素全体をスキャンして、それぞれのキャッシュされた Name をチェックします。"T" という文字で始まる名前を持つすべての子要素を、Selection に追加します (この例のデザインは、リスト ボックスまたはリスト ビュー コントロールに最も適しています)。
ページのトップへ h. UI オートメーションのクライアント向けイベントUI オートメーションでは、クライアントから目的とするイベントを購読できます。この機能により、システム内のすべての UI 要素を連続的にポーリングして、情報、構造、状態が変化していないかを確認する必要がなくなるため、パフォーマンスが向上します。 また、定義済みスコープ内のイベントのみをリッスンするため、効率も向上します。たとえば、クライアントは、ツリー内のすべての UI オートメーション要素のフォーカス変更イベント、または 1 つだけの要素とその子孫要素のフォーカス変更イベントをリッスンできます。 注: 可能性のあるイベントがすべて Microsoft UI オートメーション プロバイダーから発生するとは想定しないでください。たとえば、プロパティの変化すべてが、Windows フォーム コントロールや Win32 コントロールの標準プロキシ プロバイダーからイベントとして発生するとは限りません。 イベントを購読するクライアント アプリケーションは、AddAutomationEventHandler や AddFocusChangedEventHandler などのメソッドを使用してイベント ハンドラーに登録することによって、特定の種類のイベントを購読します。これらのメソッドでは、コールバック インターフェイスを受け取り、クライアントによって作成されるオブジェクトに実装します。その後、IUIAutomationEventHandler::HandleEvent などのメソッドが、イベントの発生時に適切なスレッドで呼び出されます。 シャットダウン時、または UI オートメーション イベントがアプリケーションの購読対象ではなくなったとき、UI オートメーション クライアントがイベントを削除します。クライアントは、RemoveAutomationEventHandler や RemoveFocusChangedEventHandler などの具体的な削除用メソッドを呼び出すか、すべてのイベントを削除する RemoveAllEventHandlers メソッドを呼び出します。 例次のコード例では、IUIAutomationFocusChangedEventHandler インターフェイスを実装するクラスの宣言を示します。
オブジェクトの IUIAutomationFocusChangedEventHandler::HandleFocusChangedEvent メソッド実装では、次の呼び出しが行われた後のすべてのフォーカス変更イベントが通知されます。
ページのトップへ 3. 設計方法と考慮事項ここでは、クライアント アプリケーション向けに UI オートメーションを使用する際の設計上の考慮事項について説明します。自動テスト用アプリケーション、画面の拡大縮小、およびセキュリティ上の問題に関しては、特に注意を払う必要があります。 a. 自動テストに UI オートメーションを使用する多くの自働テスト ツールやシナリオの目標は、ユーザー インターフェイスに対して一貫性があり、反復可能な操作を行うことです。このような操作には、特定のコントロールの単純な単体テストから、コントロールのグループに対する一連の一般的な操作を反復処理する、テスト スクリプトの記録と再生を組み合わせた複雑なテストまで含みます。 自動アプリケーションの 1 つの問題点は、動的な対象にテストを同期するのが困難な点です。たとえば、Windows タスク マネージャに含まれているような、現在実行中のアプリケーションを一覧表示するリスト ボックス コントロールを考えてみましょう。リスト ボックス内の項目はテスト アプリケーションの制御が及ぶ範囲外で動的に更新されるため、リスト ボックスの特定の項目を一貫性を保って繰り返し選択することは不可能です。テスト アプリケーションの制御が及ばない UI のフォーカス切り替えを単純に繰り返そうとする場合でも、同様の問題が起きます。 プログラムからのアクセスプログラムによるアクセスでは、従来のマウス入力やキーボード入力によって行われる相互作用や操作性を、コードで模倣する機能が提供されます。UI オートメーションは、次の 5 つのコンポーネントによりプログラムからのアクセスを可能にします。
自動テスト向けの主なプロパティUI 内の任意のコントロールを一意に識別して検索する機能は、自動テスト アプリケーションがその UI を処理する基盤となります。クライアントとプロバイダーによって使用されるいくつかの UI オートメーション プロパティが、この識別と検索を支援します。
注: AutomationId プロパティでは、オートメーション ツリー全体が一意に識別されるとは限りません。たとえば、アプリケーションには、複数のトップレベルのメニュー項目を持つメニュー コントロールが含まれ、さらに、それらのメニュー項目に複数の子メニュー項目が含まれている場合があります。これらの二次的なメニュー項目は、Item1、Item2、Item3 といった汎用的な手法で識別されることがあり、トップレベルのメニュー項目の間では、子項目の ID が重複してもかまいません。 ページのトップへ b. UI オートメーションと画面の倍率変換Windows Vista® オペレーティング システムからは、ユーザーがドット/インチ (dpi) 単位で画面の解像度を変更して、ほとんどの UI 要素を拡大表示することができます。この機能は Microsoft® Windows® クライアント オペレーティング システムでは以前から利用できていましたが、倍率変換をアプリケーションが実装している必要がありました。現在は、アプリケーションで独自の倍率変換を処理していなくても、デスクトップ ウィンドウ マネージャーが既定の倍率変換を実行します。UI オートメーション クライアント アプリケーションでは、この機能を考慮に入れる必要があります。 UI オートメーション クライアントで倍率を変換するUI オートメーション では論理座標を使用しません。次のメソッドとプロパティは、物理座標を返すか、物理座標をパラメーターとして受け取ります。
既定では、96 dpi 以外の画面解像度で実行されている UI オートメーション アプリケーションは、これらのメソッドとプロパティから正しい結果を取得できません。たとえば、カーソル位置は論理座標で表現されるため、クライアントはこれらの座標を単に IUIAutomation::ElementFromPoint メソッドに渡すだけでは、カーソルの下にある要素を取得できません。また、アプリケーションは、クライアント領域外にはウィンドウを正しく配置できません。 この解決方法は次の 2 つの部分から構成されます。
ページのトップへ 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> タグに含まれます。
(このコードの level 属性の値は、単なる一例です)。 UIAccess 属性の値は、既定で "false" に設定されます。つまり、この属性を省略した場合、またはアセンブリのマニフェストが存在しない場合、アプリケーションは保護された UI にアクセスできません。 ページのトップへ 4. 詳細情報
Web アドレスは変更される可能性があるため、ここに記載されている Web サイトに接続できない場合があります。 |
ページのトップへ