Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Доступ к контенту Modern-приложения на HTML\JS из Desktop-приложения под Windows 8

Anna | 24.06.2014 | нет комментариев

Иногда бывает необходимо из одной программы добраться до содержимого иной программы. Ну, скажем, получить из неё какой-нибудь контент, либо автоматизировать действия. В случае классических приложений Windows эта задача решается крайне легко — находим родительское окно через FindWindow, дальше, зная его HWND, можем перечислить дочерние окна и элементы управления на них. А здесь теснее полная воля — можем получить текст, написанный на этих элементах, изменить их размеры и расположение, отправить сообщения для эмуляции клика мышью либо комплекта текста с клавиатуры, даже удалить имеющиеся элементы и сделать новые.

Но для Modern-приложений всё напротив. Давайте возьмём, к примеру, приложение «Погода» из стандартного комплекта Windows8. Возможен, мы открыли его в боковой панели и хотим как-то узнать из нашего обыкновенного (Desktop) приложения, а какую же оно показывает температуру. Если посмотреть на окно «Погоды» с поддержкой Spy мы увидим родительское окно типа Windows.UI.Core.CoreWindow и вложенное в него окно Web Platform Embedding. А значит перед нами Modern-приложение написанное на HTML\Js и живущее внутри встроенного компонента браузера. То есть вышеописанные манипуляции с Windows-контролами не имеют смысла — их в этом окне просто нет, от того что всё его содержимое рендерится целиком.

Но давайте же всё-таки испробуем вытянуть из него нынешнюю температуру.

Начнём с того, что на MSDN, в его жанре, есть две статьи с противоположным оглавлением — одна предостерегает нас от того, Дабы лазить руками в IE, встроенный в чужие компоненты, потому что это опасно и дозволено всё сломать («This function is designed for internal use by Active Accessibility and is documented for informational purposes only. Neither clients nor servers should call this function. Бла-бла-бла…»). А вторая говорит, что всё ок, дозволено, и даже даёт код, как это сделать. Первая нам не интереса, а вторая — KB 249232.

Правда, в ней есть оплошность — в вызове функции ObjectFromLresult они пытаются взять неверный интерфейс и в результате ничего не работает. Но это вообще жанр MSDN, нужно привыкать.

Выходит, в чём же суть нашей затеи?

  1. Находим окно верхнего яруса с заданным заголовком и классом («Weather» и «Windows.UI.Core.CoreWindow», соответственно).
  2. Перечисляем его «детей», находим окно класса «Internet Explorer_Server».
  3. Отправляем этому окну сообщение WM_HTML_GETOBJECT, получаем в результат указатель, тот, что с поддержка функции ObjectFromLresult может быть преобразован к указателю на интерфейс IHTMLDocument2.
  4. Имея IHTMLDocument2 мы теснее можем делать с документом всё, что желательно — получить его контент, изменить, сэмулировать «клик», исполнить Javascript.

Я фактически был уверен, что где-то в районе пунктов 3-4 на пути встанет механизм безопасности Windows, отделяющий Modern-приложения друг от друга и от десктопных, и теснее был готов использовать что-то из средств, описанных мной в прошлой статье. Но… Этого не потребовалось. Невзирая на то, что приложение «Погода» работает как бы бы в песочнице, как бы бы с Low Integrity — мы спокойно можем отправлять ему сообщения, получать указатель на IHTMLDocument2, обмениваться с ним данными. Никаких барьеров безопасности преодолевать не пришлось — их легко нет.

Результат:

Стержневой код

#include "stdafx.h"
#include <iostream>
#include <sstream>
#include <mshtml.h>
#include <atlbase.h>
#include <oleacc.h>
#include "conio.h"

using namespace std;

BOOL CALLBACK EnumChildProc(HWND hwnd,LPARAM lParam)
{
	TCHAR	buf[100];

	::GetClassName( hwnd, (LPTSTR)&buf, 100 );
	if ( _tcscmp( buf, _T("Internet Explorer_Server") ) == 0 )
	{
		*(HWND*)lParam = hwnd;
		return FALSE;
	}
	else
		return TRUE;
};

void GetDocInterface(HWND hWnd) 
{
	CoInitialize( NULL );

	// Explicitly load MSAA so we know if it's installed
	HINSTANCE hInst = ::LoadLibrary( _T("OLEACC.DLL") );
	if ( hInst != NULL )
	{
		if ( hWnd != NULL )
		{
			HWND hWndChild=NULL;
			// Get 1st document window
			::EnumChildWindows( hWnd, EnumChildProc, (LPARAM)&hWndChild );
			if ( hWndChild )
			{
				CComPtr<IHTMLDocument2> spDoc;
				LRESULT lRes;

				UINT nMsg = ::RegisterWindowMessage( _T("WM_HTML_GETOBJECT") );
				::SendMessageTimeout( hWndChild, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (DWORD*)&lRes );

				LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress( hInst, "ObjectFromLresult" );
				if ( pfObjectFromLresult != NULL )
				{
					HRESULT hr;
					hr = (*pfObjectFromLresult)( lRes, IID_IHTMLDocument2, 0, (void**)&spDoc );
					if ( SUCCEEDED(hr) )
					{
						BSTR bstrContent = NULL;
						IHTMLElement *p = 0;
						spDoc->get_body(&p);

						if (p)
						{
							p->get_innerHTML( &bstrContent );
							std::wstring ws(bstrContent, SysStringLen(bstrContent));
							std::string s(ws.begin(), ws.end());
							cout << s;
							p->Release();
						}
					}
				}
			} // else document not ready
		} // else Internet Explorer is not running
		::FreeLibrary( hInst );
	} // else Active Accessibility is not installed
	CoUninitialize();
}

int _tmain(int argc, _TCHAR* argv[])
{
	wstring windowTitle, windowClass;

	wcout << "Please enter parent window title (you can find it by Spy  ):" << endl;
	std::getline(std::wcin, windowTitle);
	wcout << "Please enter parent window class (you can find it by Spy  ):" << endl;
	std::getline(std::wcin, windowClass);

	HWND hwnd = FindWindow(windowClass.c_str(), windowTitle.c_str());
	wcout << "HWND is " << hwnd << endl;

	GetDocInterface(hwnd);

	_getch();
	return 0;
}

Каждый план на Github

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB