Per Mausklick bewerten und Feedback geben
MSDN
MSDN Library
Entwicklerbibliothek
Visual Studio 2005
Visual Studio
 Erstellen von Browserhilfsobjekten ...
Erstellen von Browserhilfsobjekten mit Visual Studio 2005
Veröffentlicht: 08. Jan 2007
Von Tony Schreiner und John Sudds

In diesem Artikel wird gezeigt, wie mit Microsoft Visual Studio 2005 ein einfaches Browserhilfsobjekt (BHO) erstellt wird – ein COM-Objekt (Component Object Model), das die IObjectWithSite-Schnittstelle implementiert und sich mit Internet Explorer verknüpft. In diesem Artikel wird beschrieben, wie ein einfaches BHO schrittweise erstellt wird. Zuerst zeigt das BHO die Meldung „Hello World!“ an, während Internet Explorer ein Dokument lädt. Dann wird das BHO erweitert, um Bilder von der geladenen Seite zu entfernen. Dieser Artikel wurde für Entwickler geschrieben, die erfahren möchten, wie sie die Funktionalität des Browsers erweitern und Webentwicklertools für Internet Explorer erstellen können.

Auf dieser Seite

Einführung Einführung
Übersicht Übersicht
Einrichten des Projekts Einrichten des Projekts
Implementieren der Grundlagen Implementieren der Grundlagen
Reagieren auf Ereignisse Reagieren auf Ereignisse
Ändern des DOM Ändern des DOM
Zusammenfassung Zusammenfassung
Verwandte Themen Verwandte Themen

Einführung

Dieser Artikel basiert auf Microsoft Visual Studio 2005 und ATL (Active Template Library), um unter Verwendung von C++ ein BHO zu entwickeln. Wir haben uns entschieden, ATL zu verwenden, weil es in einfacher Weise grundlegende Codebausteine implementiert, die wir für unsere Anforderungen erweitern können. Es gibt andere Wege, ein BHO zu erstellen, wie z. B. die Verwendung von MFCs (Microsoft Foundation Classes) oder der Win32-API und des Win32-COM. ATL ist jedoch eine einfache Bibliothek, die viele Details für uns automatisch erledigt, einschließlich der Einrichtung der Registrierung mit der BHO-Klassenkennung (CLSID).

Eine andere Stärke von ATL sind seine COM-kompatiblen, intelligenten Zeigerklassen (wie z. B. CComPtr und CComBSTR), die die Gültigkeitsdauer von COM-Objekten verwalten. CComPtr ruft beispielsweise, wenn ein Wert zugewiesen wird, AddRef auf, und ruft Release auf, wenn das Objekt zerstört wird oder den Bereich verlässt. Intelligente Zeiger vereinfachen den Code und helfen, Arbeitsspeicherverluste zu vermeiden. Ihre Stabilität und Zuverlässigkeit sind besonders bei der Anwendung innerhalb des Bereichs einer einzelnen Methode nützlich.

Der erste Teil dieses Artikels führt Sie durch den Prozess der Implementierung eines einfachen BHO und der Überprüfung, ob es von Internet Explorer geladen wird. Im nächsten Teil wird veranschaulicht, wie das BHO mit Browserereignissen verknüpft wird. Im Schlussteil wird eine einfache Interaktion mit dem DHTML-DOM (Document Object Model) gezeigt, die das Erscheinungsbild einer Webseite ändert.

Übersicht

Was genau ist ein Browserhilfsobjekt (BHO)? Kurz gesagt, ist ein BHO eine einfache DLL-Erweiterung, die Internet Explorer benutzerdefinierte Funktionalität hinzufügt. Obwohl es weniger üblich und nicht Schwerpunkt dieses Artikels ist, können BHOs auch die Shellfunktionalität von Windows Explorer erweitern.

BHOs stellen üblicherweise keine eigene Benutzeroberfläche bereit. Vielmehr arbeiten sie im Hintergrund, indem sie auf Browserereignisse und Benutzereingaben reagieren. BHOs können z. B. Popups blockieren, Formulare automatisch ausfüllen oder Unterstützung für spezielle Mausbewegungen hinzufügen. Es ist ein verbreitetes Missverständnis, dass BHOs von Symbolleistenerweiterungen benötigt werden. Werden BHOs jedoch in Verbindung mit Symbolleisten verwendet, können sie die Benutzerfreundlichkeit weiter verbessern.

Hinweis  BHOs sind praktische Tools für Endbenutzer und Entwickler. Da BHOs aber erhebliche Zugriffsrechte auf den Browser und den Webinhalt haben und oft unbemerkt bleiben, müssen die Benutzer besonders darauf achten, BHOs von zuverlässigen Quellen abzurufen und zu installieren.

Die Gültigkeitsdauer eines BHO ist identisch mit der Gültigkeitsdauer der Browserinstanz, mit der es interagiert. Für Internet Explorer 6 und frühere Versionen bedeutet dies, dass für jedes neue Vordergrundfenster ein neues BHO erstellt (und zerstört) wird. Internet Explorer 7 erstellt und zerstört dagegen ein neues BHO für jede Registerkarte. BHOs werden nicht von anderen Anwendungen geladen, die das WebBrowser-Steuerelement hosten, oder von Fenstern, wie z. B. HTML-Dialogfeldern.

Die primäre Aufgabe eines BHO ist die Implementierung der IObjectWithSite-Schnittstelle. Diese Schnittstelle macht die Methode SetSite verfügbar, die die anfängliche Kommunikation mit Internet Explorer erleichtert und das BHO benachrichtigt, bevor die Freigabe erfolgt. Wir erstellen eine einfache Browsererweiterung durch Implementieren dieser Schnittstelle und fügen dann der Registrierung die CLSID des BHO hinzu.

Einrichten des Projekts

So erstellen Sie ein BHO-Projekt mit Microsoft Visual Studio 2005:

  1. Klicken Sie im Menü Datei auf Neues Projekt....
    Das Dialogfeld Neues Projekt wird angezeigt. Dieses Dialogfeld listet die Anwendungstypen auf, die Visual Studio erstellen kann.

  2. Wählen Sie unter dem Knoten „Visual C++“ die Option „ATL“ aus, sofern sie nicht bereits ausgewählt ist, und wählen Sie dann aus den Visual C++-Projekttypen die Option „ATL-Projekt“ aus. Nennen Sie das Projekt „HelloWorld“, und verwenden Sie den Standardspeicherort. Klicken Sie auf OK.

  3. Vergewissern Sie sich im ATL-Projekt-Assistenten, dass der Servertyp „Dynamic Link Library (DLL)“ lautet, und klicken Sie auf Fertig stellen.

    Jetzt hat Visual Studio Codebausteine für eine DLL erstellt. Wir fügen jetzt das COM-Objekt hinzu, das das BHO implementiert.

  4. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen Sie im Untermenü Hinzufügen die Option Klasse... aus.

  5. Wählen Sie „Einfaches ATL-Objekt“ aus, und klicken Sie auf Hinzufügen.
    Der ATL-Assistent für einfache Objekte wird angezeigt.

  6. Geben Sie im ATL-Assistenten für einfache Objekte unter Name als Kurzname „HelloWorldBHO“ ein.
    Die übrigen Namen werden automatisch eingetragen.

  7. Wählen Sie im ATL-Assistenten für einfache Objekte unter Optionen für „Threadmodell“ die Option „Apartment“ aus, für „Aggregation“ die Option „Nein“, für „Schnittstelle“ die Option „Dual“ und für „Unterstützung“ die Option „IObjectWithSite“.

    ATL-Assistent
    ATL-Assistent

  8. Klicken Sie auf Fertig stellen.

Die folgenden Dateien werden als Teil dieses Projekts erstellt.

  • HelloWorldBHO.h – diese Headerdatei enthält die Klassendefinition für das BHO.

  • HelloWorldBHO.cpp – diese Quelldatei ist die Hauptdatei für das Projekt und enthält das COM-Objekt.

  • HelloWorld.cpp – diese Quelldatei implementiert die Exporte, die das COM-Objekt über die DLL verfügbar machen.

  • HelloWorld.idl – diese Quelldatei kann verwendet werden, um benutzerdefinierte COM-Schnittstellen zu definieren. Im diesem Artikel werden wir diese Datei nicht ändern.

  • HelloWorld.rgs – diese Ressourcendatei enthält die Registrierungsschlüssel, die geschrieben bzw. entfernt werden, wenn die DLL in die Registrierung eingetragen oder daraus entfernt wird.

Implementieren der Grundlagen

Der ATL-Projekt-Assistent stellt eine Standardimplementierung von SetSite bereit. Obwohl der Schnittstellenvertrag von IObjectWithSite impliziert, dass diese Methode nach Bedarf immer wieder aufgerufen werden kann, ruft Internet Explorer diese Methode genau zweimal auf: einmal, um eine Verbindung herzustellen, und ein weiteres Mal, wenn der Browser beendet wird. Die spezielle Implementierung von SetSite in unserem BHO führt die folgenden Aktionen durch:

  • Speichern eines Verweises auf die Site. Während der Initialisierung übergibt der Browser einen IUnknown-Zeiger auf das WebBrowser-Steuerelement der obersten Ebene, und das BHO speichert einen Verweis darauf in einer privaten Membervariablen.

  • Freigeben des gerade verwendeten Sitezeigers. Wenn Internet Explorer den Wert NULL übergibt, muss das BHO alle Schnittstellenverweise freigeben und die Verbindung zum Browser trennen.

Als Teil der Verarbeitung von SetSite muss das BHO wenn erforderlich die weitere Initialisierung und Deinitialisierung durchführen. Sie können z. B. einen Verbindungspunkt für den Browser einrichten, um Browserereignisse zu empfangen.

HelloWorldBHO.h

Doppelklicken Sie, um HelloWorldBHO.h aus dem Projektmappen-Explorer von Visual Studio zu öffnen.

Schließen Sie zuerst shlguid.h ein. Diese Datei definiert Schnittstellenbezeichner für IWebBrowser2 sowie die Ereignisse, die später im Projekt verwendet werden.

#include <shlguid.h>     // IID_IWebBrowser2, DIID_DWebBrowserEvents2, etc.

Deklarieren Sie anschließend SetSite in einem öffentlichen Abschnitt der CHelloWorldBHO-Klasse.

STDMETHOD(SetSite)(IUnknown *pUnkSite);

Das STDMETHOD-Makro ist eine ATL-Konvention, die die Methode als virtuell markiert und gewährleistet, dass sie über die richtige Aufrufkonvention für die öffentliche COM-Schnittstelle verfügt. Es ist hilfreich, COM-Schnittstellen von anderen öffentlichen Methoden abzugrenzen, die ggf. in der Klasse vorhanden sind. Beim Implementieren der Membermethode wird das STDMETHODIMP-Makro analog verwendet.

Deklarieren Sie abschließend in einem privaten Abschnitt der Klassendeklaration eine Membervariable, um die Browsersite zu speichern.

private:
    CComPtr<IWebBrowser2>  m_spWebBrowser;

HelloWorldBHO.cpp

Wechseln Sie jetzt zu HelloWorldBHO.cpp, und fügen Sie für SetSite den folgenden Code ein.

STDMETHODIMP CHelloWorldBHO::SetSite(IUnknown* pUnkSite)
{
    if (pUnkSite != NULL)
    {
        // Cache the pointer to IWebBrowser2.
        pUnkSite->QueryInterface(IID_IWebBrowser2, (void**)&m_spWebBrowser);
    }
    else
    {
        // Release cached pointers and other resources here.
        m_spWebBrowser.Release();
    }

    // Return the base class implementation
    return IObjectWithSiteImpl<CHelloWorldBHO>::SetSite(pUnkSite);
}

Während der Initialisierung übergibt der Browser einen Verweis auf seine IWebBrowser2-Schnittstelle der obersten Ebene, die wir zwischenspeichern. Während der Deinitialisierung übergibt der Browser den Wert NULL. Um Arbeitsspeicherverluste und Zirkelverweiszählungen zu vermeiden, ist es wichtig, zu diesem Zeitpunkt alle Zeiger und Ressourcen freizugeben. Schließlich rufen wir die Basisklassenimplementierung auf, damit sie den restlichen Schnittstellenvertrag erfüllen kann.

HelloWorld.cpp

Wenn eine DLL geladen wird, ruft das System die DllMain-Funktion mit einer DLL_PROCESS_ATTACH-Benachrichtigung auf. Weil Internet Explorer in hohem Maße Multithreading verwendet, können häufige DLL_THREAD_ATTACH- und DLL_THREAD_DETACH-Benachrichtigungen an DllMain die Gesamtleistung der Erweiterung und des Browserprozesses herabsetzen. Da dieses BHO keine Nachverfolgung der Threadebene erfordert, können wir während der DLL_PROCESS_ATTACH-Benachrichtigung DisableThreadLibraryCalls aufrufen, um die Anzahl an Threadbenachrichtigungen gering zu halten.

Programmieren Sie in HelloWorld.cpp die DllMain-Funktion folgendermaßen:

extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        DisableThreadLibraryCalls(hInstance);
    }
    return _AtlModule.DllMain(dwReason, lpReserved); 
}

Registrieren des BHO

Nun ist nur noch der Registrierung die CLSID des BHO hinzuzufügen. Dieser Eintrag markiert die DLL als Browserhilfsobjekt und bewirkt, dass Internet Explorer das BHO beim Starten lädt. Visual Studio kann die CLSID registrieren, wenn es das Projekt erstellt.

sich, dass Sie die Entwicklungsumgebung starten, indem Sie im Startmenü mit der rechten Maustaste auf „Microsoft Visual Studio 2005“ klicken und die Option Ausführen als Administrator auswählen.

Die CLSID für dieses BHO befindet sich in HelloWorld.idl in einem Codeblock, der dem folgenden Beispiel ähnlich ist:

importlib("stdole2.tlb");
    [
        uuid(D2F7E1E3-C9DC-4349-B72C-D5A708D6DD77),
        helpstring("HelloWorldBHO Class")
    ]

Beachten Sie, dass diese Datei drei GUIDs enthält. Wir benötigen die CLSID für die Klasse und nicht jene für die Bibliothek oder die Schnittstellen-ID.

So erstellen Sie ein selbst-registrierendes BHO:

  1. Öffnen Sie HelloWorld.rgs aus dem Projektmappen-Explorer von Visual Studio.

  2. Fügen Sie am Ende der Datei den folgenden Code hinzu:

    HKLM {
      NoRemove SOFTWARE {
        NoRemove Microsoft {   
          NoRemove Windows {
            NoRemove CurrentVersion {
              NoRemove Explorer {
                NoRemove 'Browser Helper Objects' {
                  ForceRemove '{D2F7E1E3-C9DC-4349-B72C-D5A708D6DD77}' = s 'HelloWorldBHO' {
                    val 'NoExplorer' = d '1'
                  }
                }
              }
            }
          }
        }
      }
    }
    
  3. Ersetzen Sie die GUID, die oben auf ForceRemove folgt, durch die CLSID des BHO, die sich in HelloWorld.idl befindet.
    Die geschweiften Klammern dürfen nicht ersetzt werden.

  4. Speichern Sie die Datei, und erstellen Sie die Projektmappe erneut. (Drücken Sie F6.)
    Visual Studio registriert das Objekt automatisch.

Das Schlüsselwort NoRemove zeigt an, dass der Schlüssel nicht gelöscht werden soll, wenn das BHO aus der Registrierung entfernt wird. Wenn Sie dieses Schlüsselwort nicht angeben, werden leere Schlüssel entfernt. Das Schlüsselwort ForceRemove zeigt an, dass der Schlüssel und alle Werte und Unterschlüssel, die er ggf. enthält, gelöscht werden sollten. ForceRemove bewirkt zudem, dass der Schlüssel beim Registrieren des BHO neu erstellt wird, wenn er bereits existiert.

Da dieses BHO speziell für Internet Explorer entworfen wurde, geben wir den Wert NoExplorer an, um zu verhindern, dass es von Windows Explorer geladen wird. Unabhängig von Wert oder Typ lädt Windows Explorer das BHO nicht, solange der Eintrag NoExplorer vorhanden ist.

Jetzt können Sie die Projektmappe über das Menü Erstellen von Visual Studio 2005 erstellen.

Ein erster Test

Um einen schnellen Test durchzuführen, legen Sie in SetSite einen Haltepunkt fest, und starten Sie den Debugger mit der Taste F5. Wenn das Dialogfeld Ausführbare Datei für Debugsitzung angezeigt wird, wählen Sie den Standardwebbrowser aus, und klicken Sie auf OK. Wenn Internet Explorer nicht Ihr Standardbrowser ist, können Sie nach der ausführbaren Datei suchen.

Hinweis   In Windows Vista startet und beendet das Internet Explorer-Feature für den geschützten Modus einen separaten Prozess, wodurch das Debuggen ein wenig schwieriger wird. Sie können den geschützten Modus für die aktuelle Sitzung auf zwei Arten einfach abschalten: Starten Sie den Browser aus einem administrativen Prozess (wie z. B. Visual Studio), oder erstellen Sie eine lokale HTML-Datei, und geben Sie sie als Befehlszeilenparameter für Internet Explorer an.

Während der Browser startet, lädt er die DLL für das BHO. Beachten Sie, dass der pUnkSite-Parameter gesetzt wird, wenn der Haltepunkt erreicht wird. Drücken Sie die Taste F5 erneut, um das Laden der Homepage fortzusetzen.

Schließen Sie den Browser, um zu überprüfen, dass SetSite erneut mit NULL aufgerufen wird.

Reagieren auf Ereignisse

Nachdem Sie jetzt bestätigt haben, dass Internet Explorer das BHO laden und ausführen kann, können wir das Beispiel ein wenig ausbauen, indem wir das BHO so erweitern, dass es auf Browserereignisse reagiert. In diesem Abschnitt beschreiben wir die Verwendung von ATL, um einen Ereignishandler für DocumentComplete zu implementieren, der nach dem Laden der Seite ein Meldungsfeld anzeigt.

Um über Ereignisse benachrichtigt zu werden, richtet das BHO einen Verbindungspunkt mit dem Browser ein. Um auf diese Ereignisse zu reagieren, implementiert es IDispatch. Gemäß der Dokumentation für DocumentComplete hat das Ereignis zwei Parameter: pDisp (ein Zeiger auf IDispatch) und pUrl. Diese Parameter werden an IDispatch::Invoke als Teil des Ereignisses übergeben. Das manuelle Entpacken der Ereignisparameter stellt jedoch eine anspruchsvolle und fehleranfällige Aufgabe dar. Glücklicherweise stellt ATL eine Standardimplementierung bereit, die hilft, die Ereignisbehandlungslogik zu vereinfachen.

HelloWorldBHO.h

Starten Sie in HelloWorldBHO.h, indem Sie die Datei exdispid.h einschließen, in der die Verteiler-IDs für Browserereignisse definiert sind.

#include <exdispid.h> // DISPID_DOCUMENTCOMPLETE, etc.

Leiten Sie anschließend von der IDispEventImpl-Basisklasse ab, die eine einfache und sichere Alternative zu Invoke bereitstellt, um Ereignisse zu behandeln. IDispEventImpl arbeitet in Verbindung mit einer Ereignissenkenzuordnung, um Ereignisse zur entsprechenden Handlerfunktion weiterzuleiten. Wir legen fest, dass wir Ereignisse behandeln möchten, die von der DWebBrowserEvents2-Schnittstelle mit der folgenden Klassendefinition definiert worden sind (hervorgehoben).

class ATL_NO_VTABLE CHelloWorldBHO :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CHelloWorldBHO, &CLSID_HelloWorldBHO>,
    public IObjectWithSiteImpl<CHelloWorldBHO>,
    public IDispatchImpl<IHelloWorldBHO, &IID_IHelloWorldBHO, &LIBID_TestBho1Lib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
    public IDispEventImpl<1, CHelloWorldBHO, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1>

Als Nächstes fügen Sie ATL-Makros hinzu, die das Ereignis an eine neue OnDocumentComplete-Ereignishandlermethode weiterleiten, die die gleichen Argumente in der gleichen Reihenfolge entgegennimmt, wie sie durch das DocumentComplete-Ereignis definiert sind. Platzieren Sie den folgenden Code in einen öffentlichen Abschnitt der Klasse.

BEGIN_SINK_MAP(CHelloWorldBHO)
    SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)
END_SINK_MAP()

    // DWebBrowserEvents2
 void STDMETHODCALLTYPE OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL);

Die an das SINK_ENTRY_EX-Makro übergebene Zahl (1) verweist auf den ersten Parameter der IDispEventImpl-Klassendefinition und dient dazu, ggf. zwischen Ereignissen von verschiedenen Schnittstellen zu unterscheiden. Beachten Sie auch, dass Sie aus dem Ereignishandler keinen Wert zurückgeben können. Dies ist hier ohne Belang, weil Internet Explorer ohnehin Werte ignoriert, die von Invoke zurückgegeben werden.

Fügen Sie schließlich eine private Membervariable hinzu, um nachzuverfolgen, ob das Objekt eine Verbindung mit dem Browser hergestellt hat.

private:
    BOOL m_fAdvised;

HelloWorldBHO.cpp

Um den Ereignishandler mit dem Browser durch die Ereigniszuordnung zu verbinden, rufen Sie DispEventAdvise während der Verarbeitung von SetSite auf. Verwenden Sie DispEventUnadvise in gleicher Weise, um die Verbindung zu unterbrechen.

Hier ist die neue Implementierung von SetSite:

STDMETHODIMP CHelloWorldBHO::SetSite(IUnknown* pUnkSite)
{
    if (pUnkSite != NULL)
    {
        // Cache the pointer to IWebBrowser2.
        HRESULT hr = pUnkSite->QueryInterface(IID_IWebBrowser2, (void **)&m_spWebBrowser);
        if (SUCCEEDED(hr))
        {
            // Register to sink events from DWebBrowserEvents2.
            hr = DispEventAdvise(m_spWebBrowser);
            if (SUCCEEDED(hr))
            {
                m_fAdvised = TRUE;
            }
        }
    }
    else
    {
        // Unregister event sink.
        if (m_fAdvised)
        {
            DispEventUnadvise(m_spWebBrowser);
            m_fAdvised = FALSE;
        }

        // Release cached pointers and other resources here.
        m_spWebBrowser.Release();
    }

    // Call base class implementation.
    return IObjectWithSiteImpl<CHelloWorldBHO>::SetSite(pUnkSite);
}

Fügen Sie schließlich einen einfachen OnDocumentComplete-Ereignishandler hinzu.

void STDMETHODCALLTYPE CHelloWorldBHO::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL)
{
    // Retrieve the top-level window from the site.
    HWND hwnd;
    HRESULT hr = m_spWebBrowser->get_HWND((LONG_PTR*)&hwnd);
    if (SUCCEEDED(hr))
    {
        // Output a message box when page is loaded.
        MessageBox(hwnd, L"Hello World!", L"BHO", MB_OK);
    }
}

Beachten Sie, dass das Meldungsfeld das Vordergrundfenster der Site als sein übergeordnetes Fenster verwendet, statt einfach NULL in jenem Parameter zu übergeben. In Internet Explorer 6 blockiert ein übergeordnetes NULL-Fenster die Anwendung nicht. Somit kann der Benutzer fortfahren, mit dem Browser zu interagieren, während das Meldungsfeld auf Benutzereingaben wartet. In manchen Situationen kann dies dazu führen, dass der Browser nicht mehr reagiert oder abstürzt. In dem seltenen Fall, dass ein BHO eine Benutzeroberfläche anzeigen muss, muss es immer gewährleisten, dass das Dialogfeld anwendungsmodal ist, indem es ein Handle für das übergeordnete Fenster angibt.

Ein weiterer Test

Starten Sie Internet Explorer erneut mit F5. Nachdem das Dokument geladen wurde, zeigt das BHO seine Meldung an.

BHO Meldung
BHO Meldung

Setzen Sie das Browsen fort, um zu beobachten, wann und wie oft das Meldungsfeld angezeigt wird. Beachten Sie, dass die BHO-Warnung nicht nur angezeigt wird, wenn die Seite geladen wird, sondern auch, wenn die Seite durch Klicken auf die Schaltfläche Zurück erneut geladen wird. Sie wird jedoch nicht angezeigt, wenn Sie auf die Schaltfläche Aktualisieren klicken. In Internet Explorer 7 wird das Meldungsfeld für jede neue Registerkarte angezeigt.

Das Ereignis wird ausgelöst, nachdem die Seite heruntergeladen und analysiert wurde, aber bevor das window.onload-Ereignis ausgelöst wird. Im Fall mehrerer Frames wird das Ereignis mehrere Male ausgelöst, wobei zum Schluss der Vordergrundframe folgt. Im folgenden Code ermitteln wir das letzte Ereignis einer Serie durch Vergleichen des im pDisp-Parameter des Ereignisses übergebenen Objekts an den Vordergrundbrowser, der in SetSite zwischengespeichert wurde.

Ändern des DOM

Der folgende JavaScript-Code veranschaulicht eine einfache Änderung des DOM. Er blendet Bilder auf der Webseite aus, indem er das display-Attribut des Stilobjekts des Bilds auf „none“ setzt.

function RemoveImages(doc)
{
    var images = doc.images;
    if (images != null)
    {
        for (var i = 0; i < images.length; i++) 
        {
            var img = images.item(i);
            img.style.display = "none";
        }
    }
}

In diesem letzten Abschnitt wird gezeigt, wie Sie diese einfache Logik in C++ implementieren.

HelloWorldBHO.h

Öffnen Sie zuerst HelloWorldBHO.h, und schließen Sie mshtml.h ein. Diese Headerdatei definiert die Schnittstellen, die wir für die Arbeit mit dem DOM brauchen.

#include <mshtml.h>         // DOM interfaces

Als Nächstes definieren Sie die private Membermethode so, dass sie die C++-Implementierung des obigen JavaScript-Codes enthält.

private:
    void RemoveImages(IHTMLDocument2 *pDocument);

HelloWorldBHO.cpp

Der OnDocumentComplete-Ereignishandler hat jetzt zwei neue Funktionen. Als Erstes vergleicht er den zwischengespeicherten WebBrowser-Zeiger mit dem Objekt, für den das Ereignis ausgelöst wird. Wenn die Werte gleich sind, betrifft das Ereignis das Vordergrundfenster, und das Dokument ist vollständig geladen. Als Zweites ruft er einen Zeiger auf das document-Objekt auf und übergibt ihn an RemoveImages.

void STDMETHODCALLTYPE CHelloWorldBHO::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL)
{
    HRESULT hr = S_OK;

    // Query for the IWebBrowser2 interface.
    CComQIPtr<IWebBrowser2> spTempWebBrowser = pDisp;

    // Is this event associated with the top-level browser?
    if (spTempWebBrowser && m_spWebBrowser &&
        m_spWebBrowser.IsEqualObject(spTempWebBrowser))
    {
        // Get the current document object from browser...
        CComPtr<IDispatch> spDispDoc;
        hr = m_spWebBrowser->get_Document(&spDispDoc);
        if (SUCCEEDED(hr))
        {
            // ...and query for an HTML document.
            CComQIPtr<IHTMLDocument2> spHTMLDoc = spDispDoc;
            if (spHTMLDoc != NULL)
            {
                // Finally, remove the images.
                RemoveImages(spHTMLDoc);
            }
        }
    }
}

Der IDispatch-Zeiger in pDisp enthält die IWebBrowser2-Schnittstelle des Fensters oder Frames, in dem das Dokument geladen wurde. Wir speichern den Wert in einer CComQIPtr-Klassenvariablen, die automatisch ein QueryInterface ausführt. Um als Nächstes zu bestimmen, ob die Seite vollständig geladen wurde, vergleichen wir den Schnittstellenzeiger mit demjenigen, den wir in SetSite für den Vordergrundbrowser zwischengespeichert haben. Als Ergebnis dieses Tests werden nur Bilder von Dokumenten im Vordergrundbrowserframe entfernt. Dokumente, die nicht im Vordergrundframe geladen werden, bestehen diesen Test nicht. (Weitere Informationen finden Sie unter How To Determine When a Page Is Done Loading in WebBrowser Control und How to get the WebBrowser object model of an HTML frame.)

Um das HTML-Objekt document abzurufen, sind zwei Schritte erforderlich. Weil get_Document einen Zeiger für das aktive Dokument abruft, selbst wenn der Browser ein Dokumentobjekt eines anderen Typs (wie z. B. ein Microsoft Word-Dokument) gehostet hat, müssen wir das aktive Dokument noch auf eine IHTMLDocument2-Schnittstelle abfragen, um zu bestimmen, ob es tatsächlich eine HTML-Seite ist. Die IHTMLDocument2-Schnittstelle stellt den Zugriff auf den Inhalt des DHTML-DOM bereit.

Nachdem bestätigt ist, dass ein HTML-Dokument geladen wurde, übergeben wir den Wert an RemoveImages. Beachten Sie, dass das Argument an IHTMLDocument2 als Zeiger übergeben wird und nicht als CComPtr.

void CHelloWorldBHO::RemoveImages(IHTMLDocument2* pDocument)
{
    CComPtr<IHTMLElementCollection> spImages;

    // Get the collection of images from the DOM.
    HRESULT hr = pDocument->get_images(&spImages);
    if (hr == S_OK && spImages != NULL)
    {
        // Get the number of images in the collection.
        long cImages = 0;
        hr = spImages->get_length(&cImages);
        if (hr == S_OK && cImages > 0)
        {
            for (int i = 0; i < cImages; i++)
            {
                CComVariant svarItemIndex(i);
                CComVariant svarEmpty;
                CComPtr<IDispatch> spdispImage;

                // Get the image out of the collection by index.
                hr = spImages->item(svarItemIndex, svarEmpty, &spdispImage);
                if (hr == S_OK && spdispImage != NULL)
                {
                    // First, query for the generic HTML element interface...
                    CComQIPtr<IHTMLElement> spElement = spdispImage;

                    if (spElement)
                    {
                        // ...then ask for the style interface.
                        CComPtr<IHTMLStyle> spStyle;
                        hr = spElement->get_style(&spStyle);

                        // Set display="none" to hide the image.
                        if (hr == S_OK && spStyle != NULL)
                        {
                            static const CComBSTR sbstrNone(L"none");
                            spStyle->put_display(sbstrNone);
                        }
                    }
                }
            }
        }
    }
}

Das Interagieren mit dem DOM ist in C++ ausführlicher als in JavaScript, der Codefluss ist jedoch im Wesentlichen gleich.

Der vorhergehende Code wird für jedes Element in der Bildersammlung erneut ausgeführt. Im JavaScript ist klar, ob auf das Sammlungselement per Ordnungszahl oder per Name zugegriffen wird. In C++ müssen Sie diese Argumente jedoch durch Übergeben einer leeren Variante manuell eindeutig deklarieren. Wir verlassen uns wieder auf eine ATL-Hilfsklasse, dieses Mal ist es CComVariant, um den Umfang des Codes zu minimieren, den wir selbst schreiben müssen.

Schlussbemerkungen

Um das Skripting zu vereinfachen, verwenden alle Objekte im DOM IDispatch. Hierdurch werden Eigenschaften und Methoden verfügbar gemacht, die von mehreren Schnittstellen abgeleitet werden. In C++ müssen Sie jedoch explizit für die Schnittstelle abfragen, die die Eigenschaft oder die Methode unterstützt, die Sie verwenden möchten. Ein Bildobjekt unterstützt z. B. die beiden Schnittstellen IHTMLElement und IHTMLImgElement. Um ein Stilobjekt für ein Bild abzurufen, müssen Sie daher zuerst für eine IHTMLElement-Schnittstelle abfragen, die die get_style-Methode verfügbar macht.

Beachten Sie auch, dass COM-Regeln im Falle eines Fehlers keinen gültigen Zeiger garantieren. Daher müssen Sie nach jedem COM-Aufruf den Wert von HRESULT überprüfen. Außerdem stellt es für viele DOM-Methoden keinen Fehler dar, einen NULL-Wert zurückzugeben. Deshalb müssen Sie darauf achten, den Rückgabewert und den Zeigerwert zu überprüfen. Um die Überprüfung noch sicherer zu machen, initialisieren Sie den Zeiger im Voraus immer auf NULL. Die Anwendung eines defensiven, ausführlichen und fehlertoleranten Programmierstils kann helfen, später unvorhersehbare Fehler zu verhindern.

Zusammenfassung

Es gibt verschiedene Typen von BHOs mit einem weiten Verwendungsbereich. Alle BHOs weisen jedoch ein gemeinsames Feature auf: eine Verbindung zum Browser. Wegen ihrer Fähigkeit, sich eng in Internet Explorer zu integrieren, werden BHOs von vielen Entwicklern geschätzt, die die Funktionalität des Browsers erweitern möchten. In diesem Artikel wurde veranschaulicht, wie ein einfaches BHO erstellt wird, das die Stilattribute von IMG-Elementen in einem geladenen Dokument ändert. Sie können dieses Einstiegsbeispiel gern nach Ihren Wünschen erweitern. Entdecken Sie weitere Möglichkeiten durch Aufrufen der folgenden Links.

Verwandte Themen


© 2008 Microsoft Corporation. Alle Rechte vorbehalten. Nutzungsbedingungen  |  Markenzeichen  |  Informationen zur Datensicherheit
Page view tracker