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
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.
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
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
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.
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.
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);
}
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;
}