2013-04-17 8 views
11

[noch einmal überarbeitet für Klarheit]Wie automatisiere ich eine IE-Webanwendung, die einen modalen HTML-Dialog öffnet?

Ich habe ein C++ Programm, das mit einer Website interagiert. Die Seite ist IE-spezifisch und mein Programm auch.

Ich verbinde die laufende Instanz von IE auf eine gewöhnliche Weise (aus Prozess - siehe Code). Sobald ich die IWebBrowser2 bekomme, habe ich kein Problem, die IHTMLDocument2 und Interaktion mit den einzelnen IHTMLElement Objekte, Felder ausfüllen und klicken Sie auf Schaltflächen.

Aber wenn die Webseite Javascript hat, die window.showModalDialog aufruft, stecke ich fest: Ich muss mit den HTML-Elementen im Popup interagieren, genau wie die anderen Seiten; aber ich kann nicht seine IWebBrowser2 bekommen.

Das Popup ist immer "Webseitendialog" und ist ein Fenster vom Typ Internet Explorer_TridentDlgFrame mit einem Internet Explorer_Server. Aber ich kann nicht den IWebBrowser2 aus dem Internet Explorer_Server Fenster so wie ich kann, wenn es eine normale IE-Instanz ist.

Ich kann die IHTMLDocument2Ptr bekommen, aber wenn ich versuche, die IWebBrowser2 ich eine HRESULT von E_NOINTERFACE bekommen zu bekommen.

Der Code ziemlicher Standard ist und funktioniert gut, wenn es sich um eine 'normale' IE-Fenster ist

IHTMLDocument2Ptr pDoc; 
LRESULT lRes; 

/* hWndChild is an instance of class "Internet Explorer_Server" */ 

UINT nMsg = ::RegisterWindowMessage("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_IHTMLDocument, 0, (void**)&pDoc); 
    if (SUCCEEDED(hr)) { 
     IServiceProvider *pService; 
     hr = pDoc->QueryInterface(IID_IServiceProvider, (void **) &pService); 
     if (SUCCEEDED(hr)) 
     { 
      hr = pService->QueryService(SID_SWebBrowserApp, 
       IID_IWebBrowser2, (void **) &pBrowser); 

      // This is where the problem occurs: 
      // hr == E_NOINTERFACE 
     } 
    } 
} 

Falls es darauf ankommt, ist dies Vista und IE8. (Ich betone dies, weil beide diese brechenden Änderungen in meiner Codebasis einführten, die mit XP/IE7 gut gearbeitet hatten.)

Noch einmal, mein Ziel ist, jedes IHTMLElement10 zu bekommen und mit ihm zu interagieren. Ich habe keinen Zugriff auf den Quellcode der Anwendung, die ich automatisiere.

Ich überlege, senden Tastenanschläge blind zu dem Internet Explorer_Server Fenster, würde aber eher nicht.

Edited hinzufügen:

Jemand schlug vor, die Kind-Fenster bekommen und ihnen Nachrichten senden, aber ich bin mir ziemlich sicher, dass nicht mit Internet Explorer_Server funktioniert; Laut Spy ++ gibt es keine untergeordneten Fenster. (Dies ist nicht IE-spezifisch. Java-Applets scheinen nicht untergeordnete Fenster zu haben, auch nicht.)

aktualisieren

In den Kommentaren, sagte Simon Maurer der obige Code für ihn gearbeitet, und nur zu Stellen Sie sicher, dass es keine Tippfehler gab. Er hat sehr großzügig eine vollständige Standalone-App auf pastebin veröffentlicht. Als ich seinen Code benutzte, scheiterte er auf die gleiche Weise an der gleichen Stelle, und mir wurde klar, dass er dachte, ich wollte mich mit der zugrunde liegenden Seite verbinden, nicht mit dem Popup. Also habe ich den obigen Text bearbeitet, um diese Mehrdeutigkeit zu beseitigen.

+0

Was ist die Ausnahme? Erscheint 'pDoc' beim Aufruf von' pDoc-> QueryInterface'? –

+0

@NateHekman: Ich habe die Frage wesentlich überarbeitet. – egrunin

+0

Können Sie bestätigen, dass die C++ App nicht in Bearbeitung ist? Was ist ein "Webseiten-Dialog"? Ist es das IE-Fenster, das erscheint, wenn ein Skript showModalDialog aufruft? –

Antwort

3

Ich weiß nicht, warum Sie die IServiceProvider oder IWebBrowser2 erhalten möchten, wenn Sie nur die IHTMLElement 's wollen. Sie können sie erhalten, indem Sie die IHTMLDocumentget_all() Methode aufrufen.

Dieser Code-Schnipsel zeigt Ihnen, wie das funktioniert:

#include <Windows.h> 
#include <mshtml.h> 
#include <Exdisp.h> 
#include <atlbase.h> 
#include <SHLGUID.h> 
#include <oleacc.h> 
#include <comdef.h> 
#include <tchar.h> 

HRESULT EnumElements(HINSTANCE hOleAccInst, HWND child) 
{ 
    HRESULT hr; 

    UINT nMsg = ::RegisterWindowMessage(_T("WM_HTML_GETOBJECT")); 
    LRESULT lRes = 0; 
    ::SendMessageTimeout(child, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (PDWORD)&lRes); 

    LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress(hOleAccInst, "ObjectFromLresult"); 
    if (pfObjectFromLresult == NULL) 
     return S_FALSE; 

    CComPtr<IHTMLDocument2> spDoc; 
    hr = (*pfObjectFromLresult)(lRes, IID_IHTMLDocument2, 0, (void**)&spDoc); 
    if (FAILED(hr)) return hr; 

    CComPtr<IHTMLElementCollection> spElementCollection; 
    hr = spDoc->get_all(&spElementCollection); 
    if (FAILED(hr)) return hr; 

    CComBSTR url; 
    spDoc->get_URL(&url); 
    printf("URL: %ws\n", url); 

    long lElementCount; 
    hr = spElementCollection->get_length(&lElementCount); 
    if (FAILED(hr)) return hr; 
    printf("Number of elements: %d", lElementCount); 

    VARIANT vIndex; vIndex.vt = VT_I4; 
    VARIANT vSubIndex; vSubIndex.vt = VT_I4; vSubIndex.lVal = 0; 
    for (vIndex.lVal = 0; vIndex.lVal < lElementCount; vIndex.lVal++) 
    { 
     CComPtr<IDispatch> spDispatchElement; 
     if (FAILED(spElementCollection->item(vIndex, vSubIndex, &spDispatchElement))) 
      continue; 
     CComPtr<IHTMLElement> spElement; 
     if (FAILED(spDispatchElement->QueryInterface(IID_IHTMLElement, (void**)&spElement))) 
      continue; 
     CComBSTR tagName; 
     if (SUCCEEDED(spElement->get_tagName(&tagName))) 
     { 
      printf("%ws\n", tagName); 
     } 
    } 
    return S_OK; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    ::CoInitialize(NULL); 
    HINSTANCE hInst = ::LoadLibrary(_T("OLEACC.DLL")); 
    if (hInst != NULL) 
    { 
     HRESULT hr = EnumElements(hInst, (HWND)0x000F05E4); // Handle to Internet Explorer_Server determined with Spy++ :) 
     ::FreeLibrary(hInst); 
    } 
    ::CoUninitialize(); 
    return 0; 
} 

Above-Code auf beide Werke: ein normales Fenster oder ein modales Fenster, geben Sie einfach die richtige HWND an die SendMessageTimeout Funktion.

WARNUNG Ich benutze eine hartcodierte HWND Wert in diesem Beispiel, wenn Sie es testen möchten, sollten Sie eine IE Instanz starten und die HWND des Internet Explorer_Server Fenster mit Spy ++ erhalten.

Ich rate Ihnen auch, CComPtr zu verwenden, um Speicherlecks zu vermeiden.

+0

Ich werde das versuchen. Welche Version (en) von IE verwenden Sie? – egrunin

+0

IE10 unter Windows 7 x64 –

+0

IE8 unter Windows XP 32bit funktioniert auch –

Verwandte Themen