Microsoft Active Accessibilityを使用してブラウザーコンテンツにアクセスする

このような単純なタスクの解決策を考えてみましょう。

ユーザーが既に起動しているブラウザ(IE、Chrome、Firefox)。

必須 :現在アドレスバーに入力されているURLを受け取るプログラムを作成します。



この単純なタスクが機能しないことを解決する方法について考えてみましょう。



1. FindWindow + GetWindowText

なぜ機能しない
最初のアイデアは、ブラウザウィンドウ、つまりアドレスバーの子ウィンドウを見つけ、そこからURLを取得することです。 練習では、IEのみがアドレスバー用に別の子ウィンドウを持つことを示しています。 FFとChromeはクロスプラットフォームなので、すべてのコンテンツを自分でレンダリングすることを好みます。


2.プログラムにURLを返すブラウザー拡張機能(たとえば、localhostへの要求を通じて)

なぜ機能しない
できます。 しかし、まず、3つのブラウザーの場合、3つの異なる拡張機能を作成する必要があります。2番目に、FFとChromeの場合は、拡張ストアを通じてのみ配布する必要があります。 パフォーマンスがモデレーターの左のかかとが今日コーミングされるかどうかに依存するプログラムを作成するには-いいえ、ありがとう。


3.スニッファーを作成して、ユーザーがそこで開いたものを見てみましょう

なぜ機能しない
さあ! しかし、次は何ですか? ブラウザーがトラフィックストリームから受信したデータを選択し、HTTPプロトコルを解読しても、 現在の URLは認識されません(ストリームには多くのリンクがあります)。 さらに、HTTPS接続、HTTP / 2、ローカルで開くファイルへのリンク、内部ページ( chrome:// settingsなど)へのリンクなどの庭にすぐに行きます


4. リモートデバッグプロトコルまたはSeleniumを使用しましょう

なぜ機能しない
元のタスクの条件の制限のために適切ではありません。ブラウザは既に実行されているため、新しい制御されたインスタンスを起動できません。既存のインスタンスと対話する必要があります。


5.たぶんフック

なぜ機能しない
さて、ブラウザに侵入することができます。 フックとは何ですか? IEの場合、すべてが明確です-アドレスバーウィンドウのSetWindowText(ただし、それにより、簡単な方法1が実行されました)。 FFとChromeでは、明確に定義されたオブジェクトやインターフェイスを使用できません。 特定のバージョンのブラウザで何かをすることはできますが、普遍的なソリューションは機能しません。


6.ブラウザウィンドウのスクリーンショット、アドレスバーの位置の決定、画像からのテキストの認識!

なぜ機能しない
すでにどういうわけか絶望のように見え始めますよね? OSのカラースキーム、権限、スケールのすべてのオプションを見て、プラグインの存在、カラースキーム、要素の非標準配置、ブラウザーの右から左への言語ロケールを考慮し、アドレスバーウィンドウが狭すぎてURLを完全に収容できない場合を終了します。


7.オプション

そして、他の決定があなたの頭に浮かぶことをコメントに書いてください、そして、それがうまくいくかどうかについて考えます。



そして今、正しい答えの1つです.Microsoft Active Accessibilityテクノロジーを使用します 。これは既に古いですが、非常に安定しており、Win95からWin10までのすべてのオペレーティングシステムのすべてのブラウザーでサポートされています。これにより、現在のURLを取得するだけでなく(すべてのブラウザーで同じように)、ただし、通常は、すべてのブラウザコンテンツ(タイトル、メニュー、ツールバー、タブを持つ親ウィンドウから、最後の要素まで開いているWebページのコンテンツ)にアクセスできます。



はじめに



Microsoft Active Accessibility(MSAA)は、1997年に既に発明されており、画面拡大鏡、画面からテキストを読み取るアプリケーション、および障害を持つ人々とコンピューターの相互作用を改善する他のプログラムを作成することを可能にしました(視覚の問題、ヒアリングなど)。 IEのテクノロジーサポートはずっと前に登場しましたが、FFとChromeでも少し後に追加されました。 Vistaのリリースにより、WindowsオートメーションAPIが改善されましたが、古き良きMSAAはなくなりません。最新のOSおよびブラウザーで正常に動作します。



コード



一般に、コードには複雑なものはありません。 エントリポイントは親ブラウザウィンドウになり、ClassIDで取得できます。

FindWindow(L"IEFrame", NULL); // IE FindWindow(L"MozillaWindowClass", NULL); // Firefox FindWindow(L"Chrome_WidgetWin_1", NULL); // Chrome.    ,  -  (http://www.chromium.org/developers/design-documents/accessibility)    ,     "Chrome",  ,        .    ,       class name   .
      
      







次に、このウィンドウからIAccessible COMインターフェイスへのポインターを取得する必要があります

 ::AccessibleObjectFromWindow(hWndChrome, OBJID_CLIENT, IID_IAccessible, (void**)(&pAccMain));
      
      







はい、その前に、忘れないでください:





したがって、IAccessibleへのポインターがあります。 これは何ですか これは、ブラウザ全体(ウィンドウ、タイトル、メニュー、ツールバー、アドレスバー、ページコンテンツ、ステータスバー)を記述するツリーのルートノードです。 これを視覚的な形でどのように見ますか? これ以上簡単なことはありません! Microsoftはこのためにinspect.exeユーティリティを提供しています(Windows SDKに付属しています。C:\ Program Files(x86)\ Windows Kits \ 8.0 \ bin \ x64フォルダにあります)。 Chromium開発者はaViewerを推奨します



利用可能なブラウザ要素のツリーがどのように見えるか見てみましょう:

IE





クロム





Firefox





ご覧のとおり、アドレスバーにはすべてのブラウザーのIAccessibleインターフェイスからアクセスできます。 要素の名前、異なるブラウザーでのツリー内の位置は異なりますが、一般に、アドレスバーにアクセスするには、現在の要素の名前と値を取得する機能、およびツリーの現在の要素の子を取得する機能のみが必要です。



どちらも簡単に記述できます。Chromeの現在のURLを取得する最終的なコードを次に示します。



 #include "stdafx.h" #include <string> #include <iostream> #include "windows.h" #include "oleacc.h" #include "atlbase.h" std::wstring GetName(IAccessible *pAcc) { CComBSTR bstrName; if (!pAcc || FAILED(pAcc->get_accName(CComVariant((int)CHILDID_SELF), &bstrName)) || !bstrName.m_str) return L""; return bstrName.m_str; } HRESULT WalkTreeWithAccessibleChildren(CComPtr<IAccessible> pAcc) { long childCount = 0; long returnCount = 0; HRESULT hr = pAcc->get_accChildCount(&childCount); if (childCount == 0) return S_OK; CComVariant* pArray = new CComVariant[childCount]; hr = ::AccessibleChildren(pAcc, 0L, childCount, pArray, &returnCount); if (FAILED(hr)) return hr; for (int x = 0; x < returnCount; x++) { CComVariant vtChild = pArray[x]; if (vtChild.vt != VT_DISPATCH) continue; CComPtr<IDispatch> pDisp = vtChild.pdispVal; CComQIPtr<IAccessible> pAccChild = pDisp; if (!pAccChild) continue; std::wstring name = GetName(pAccChild).data(); if (name.find(L"    ") != -1) { CComBSTR bstrValue; if (SUCCEEDED(pAccChild->get_accValue(CComVariant((int)CHILDID_SELF), &bstrValue)) && bstrValue.m_str) std::wcout << std::wstring(bstrValue.m_str).c_str(); return S_FALSE; } if (WalkTreeWithAccessibleChildren(pAccChild) == S_FALSE) return S_FALSE; } delete[] pArray; return S_OK; } HWND hWndChrome = NULL; BOOL CALLBACK FindChromeWindowProc(HWND hwnd, LPARAM lParam) { wchar_t className[100]; if (GetClassName(hwnd, className, 100) == 0 || wcscmp(className, L"Chrome_WidgetWin_1") != 0) return TRUE; wchar_t title[1000]; if (GetWindowText(hwnd, title, 1000) == 0 || wcslen(title) == 0) return TRUE; hWndChrome = hwnd; return FALSE; } int _tmain(int argc, _TCHAR* argv[]) { ::CoInitialize(NULL); EnumWindows(FindChromeWindowProc, 0); if (hWndChrome == NULL) return 0; CComPtr<IAccessible> pAccMain; HRESULT hr = ::AccessibleObjectFromWindow(hWndChrome, 1, IID_IAccessible, (void**)(&pAccMain)); // 1 -    CComPtr<IAccessible> pAccMain2; ::AccessibleObjectFromWindow(hWndChrome, OBJID_CLIENT, IID_IAccessible, (void**)(&pAccMain2)); WalkTreeWithAccessibleChildren(pAccMain2); return 0; }
      
      







仕事の結果:







他のブラウザでは、すべてが似ています。



小さなニュアンス



ChromeのMSAAテクノロジーはデフォルトで無効になっています。 これは、Chromeのアーキテクチャによるものです。プロセスへの分割は、MSAAが必要とする要素ツリー全体に関する情報が1つのプロセスに存在しないという事実につながります。 Chromeの開発者は愚か者ではなく、この情報のコレクションとそのキャッシュをメインプロセスに含めるために提供されています。 しかし、これはすべていくらかリソースを消費し、MSAAテクノロジーを必要とする人は比較的少ないため、デフォルトでオフにしました。 次の2つの方法で有効にできます。




All Articles