CppWebBrowser i AJAX

0

Witam,

W jaki sposób dostać to co odbiera komponent WebBrowser - przy czym kompletne załadowanie strony nie pomaga bo później dochodzą dane które są uaktualniane (technologia AJAX) bez przeładowywania strony.

Pozdrawiam

0

EDIT: poprawiłem typo w StringFromCLSID2.

Jeżeli czujesz się na siłach, to możesz czytać dalej.
W DocumentComplete pobierasz wskaźnik obiektu ajax (znając nazwę zmiennej która go przechowuje) za pomocą document->{script->}invoke i podmieniasz adresy wybranej lub wszystkich metod które zwracają "wynik" - get_responseXML, get_responseText, get_responseBody, get_responseStream.

Problemem tutaj jest jedynie konieczność znania nazwy zmiennej która przechowuje IXMLHTTPRequest.

Sposób 2. Tworzysz klasę która implementuje wszystkie metody ajaxa, tzn jest klasą typu proxy - każda metoda wywołuje tą samą metodę w oryginalnym objekcie. Pakujesz to w dll i rejestrujesz w rejestrze pod dowolnym (nowym) CLSID. Teraz w programie z browserem gdzieś na początku używasz funkcji CoTreatAsClass by przekierować tworzenie nowego obiektu ajaxa na swoją klasę, która w jeden z magicznych sposobów komunikuje się z aplikacją.

Sposób 3. Ustawiasz breakpoint na początku funkcji CoCreateInstance i za pomocą AddVectoredExceptionHandler (koniecznie) dodajesz exceptions-handler, który sprawdza czy CLSID jest ajaxem. Jeżeli jest, to podstawiasz swoją klasę (CMyAjax).

struct COCREATEINSTANCE
{
	DWORD dwReturnAddress;
	CLSID *rclsid;
	IUnknown *pUnkOuter;
	DWORD dwClsContext;
	IID *riid;
	LPVOID *ppv;
};

LONG __stdcall MyHandler(EXCEPTION_POINTERS* ExceptionInfo)
{
   WCHAR wszClsid[64];
   if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
   {
      // breakpoint: (int3+nop) zastępuje rozkaz mov ebp,ebp
      CONTEXT *ctx = ExceptionInfo->ContextRecord;
      if (ExceptionInfo->ExceptionRecord->ExceptionAddress == &CoCreateInstance)
      {
         COCREATEINSTANCE *stack = (COCREATEINSTANCE*)(ctx->Esp);
         StringFromCLSID2(stack->rclsid, wszClsid, 64);
         // sprawdź czy wszClsid jest 'ajaxem'
         if (ajax)
         {
            *(stack->ppv) = (void*)new CMyAjax;
            ctx->Eax = 0; // S_OK
            ctx->Esp += sizeof(COCREATEINSTANCE);
            ctx->Eip = stack->dwReturnAddress;
         }
         else
         {
            ctx->Eip++;
         }
         return EXCEPTION_CONTINUE_EXECUTION;
      }
   }
   return EXCEPTION_CONTINUE_SEARCH;
}

Następny sposób. Za pomocą CoCreateInstance tworzysz ajaxa i hookujesz jego metody w vftable, wpisując adresy własnych funkcji. Teraz cokolwiek w Twoim procesie wywoła get_response* - dowiesz się o tym w funkcji hooka gdzie powinnaś najpierw wywołać oryginalną metodę, zrobić co tam chcesz i po prostu wrócić return'em.

0

Hmm trochę to wszystko skomplikowane (sposób nr trzy absolutnie nie wiem o co chodzi) - a możesz podać chociaż linka do jakiegoś przykładu - może najłatwiej z metodą pierwszą.
To by mi wiele pomogło.

Pozdrawiam

0

Konkretnego przykładu raczej nie znajdziesz, raczej zbiór puzli które trzeba poskładać by coś wyszło. Ale masz Glück, miałem chwilę i napisałem malutką aplikację. Przeczytaj readme.exe zanim zaczniesz analizować resztę.
Wbrałem tu sposób z CoCreateInstance + hookowanie metod i callbacka ajax::readystateChange, a dopiero potem browser i nawigacja.

Jako identyfikator ajaxa wstawiłem na sztywno "Microsoft.XMLHTTP", więc u Ciebie może nie zadziałać dla innych wersji niż ta, która u mnie jest domyślna.

http://www.sendspace.com/file/1a1tsu [2011.07.05]
a tu screen http://i43.tinypic.com/2gvmxih.png

0

Jestem pod wrażeniem - kompletnie nie wiem o co tam biega (ech niestety taka moja wiedza) w tym kodzie ale powoli zaczynam analizować. Mam nadzieje że coś mi się uda z tego stworzyć. A zastanawiam się dlaczego powyższy program nie działa na VS2008. - otrzymuje błąd kompilacji przy deklaracji QISearch.

Dzięki,
Pozdrawiam

0

QISearch jest w shlwapi.h, ale brakuje jego deklaracji w VS2005 na którym to pisałem.
Duplikat jest w unknown.h. Zapewne wystarczy usunąć #include shlwapi z unknown.cpp.

0

A więc tak postanowiłam że dorzucę parę rzeczy od siebie do tego projektu aby jak najmniej modyfikować (mam już VS2005 i jest ok) - jednak patrze i to jest WinApi (czyli nie moja działka).

Czy jest możliwość żeby w C++ pod visualem odwoływał się do okienka poprzez Form1->Caption itp (do pozostałych elementów też) - tak samo jak w borlandzie.

0

tak. musi po prostu apriori znac wskaznik lub referencje prowadzaca do owego Form1. pod borlandem IIRW istnialy automatyczne globalne wskazniki na (singletony?) form, tutaj - sama to musisz zrobic

0

Albo może nie przerabiaj mojego kodu tylko użyj kilka funkcji z niego - np. InstallHook wywołujesz przed otwarciem swojego okienka (lub przed otwarciem pierwszej strony z ajaxem), a ReleaseHooks() zanim zniszczysz browser.

Dalej masz funkcje Ajax* ... i zmienne origAjax* - skopiuj je do swojego programu i zamiast AddLogEntry, użyj jakiejś innej funkcji do print'owania ciekawych informacji, np. memo.lines.add.
Funkcja AjaxPutOnreadystatechange i AjaxInvoke nie niosą ze sobą nic ciekawego, można je usunąć (z funkcji InstallHook też)

Cała reszta jest tylko po to, by było okienko z browserem.

0

A wiec jednak lepiej jak wykorzystam pewne fragmenty kodu z owego przykładu. Ale mam kolejne pytanie -
mam pewna funkcję ajax która jest wywoływana z parametrem po kliknięciu przycisku na stronie web - i czy mogę ta funkcje wywołać.

Przykładowo:

string s = "ala ma kota";
if (cos == costam)
{
      ajax_fun(s);
}
0

Dla takiego skryptu

<script>function functionname(x)
{
	alert(x);
}
</script>

Zrobisz to funkcją

ExecMethod(L"functionname", L"demo parameter");

A kod jest tutaj:

HRESULT GetScript(IDispatch **ppv)
{
	HRESULT hr = E_FAIL;

	IDispatch *disp;
	if (g_browser && !g_browser->get_Document(&disp) && disp)
	{
		IHTMLDocument *doc;
		if (!disp->QueryInterface(IID_IHTMLDocument, (void**)&doc))
		{
			hr = (!(!doc->get_Script(ppv) && *ppv));
			doc->Release();
		}
		disp->Release();
	}
	return hr;
}

HRESULT GetSymbolId(IDispatch *script, LPWSTR pwszName, DISPID *ppv)
{
	return script->GetIDsOfNames(IID_NULL, &pwszName, 1, 0, ppv);
}

HRESULT ExecMethod(LPWSTR pwszMethodName, LPWSTR pwszParameter, VARIANT *pResult=0)
{
	DISPPARAMS dparams;
	IDispatch *script;
	VARIANTARG args[1];

	if (pResult) pResult->vt = VT_EMPTY;

	HRESULT hr = GetScript(&script);
	if (!hr)
	{
		DISPID id;
		hr = GetSymbolId(script, pwszMethodName, &id);
		if (!hr)
		{
			args[0].vt      = VT_BSTR;
			args[0].bstrVal = SysAllocString(pwszParameter);

			dparams.cArgs             = 1;
			dparams.cNamedArgs        = 0;
			dparams.rgdispidNamedArgs = 0;
			dparams.rgvarg            = args;
			hr = script->Invoke(id, IID_NULL, 0, DISPATCH_METHOD, &dparams, pResult, 0, 0);

			VariantClear(&args[0]);
		}
		script->Release();
	}
	return hr;
}
0

Jeszcze tego kodu nie sprawdzałam ale jestem przekonana że działa :-)

Wielkie podziękowania dla sapero który tak wiele mi pomógł.

Pozdrawiam :-)

1 użytkowników online, w tym zalogowanych: 0, gości: 1