207 votes

Développement d'extensions Internet Explorer?

Après avoir développé un peu de Firefox Et les extensions de Chrome, j'ai décidé d'essayer et d'élargir mes compétences par le développement d'un Internet Explorer extension en C#. Je suis allé vers elle en pensant qu'il ne serait pas trop mal.

Wow.

J'étais vraiment mal. Donc, ma question est, est-ce que quelqu'un a de l'expérience avec les/dans le développement de IE extensions qui peuvent partager leurs connaissances? Cela pourrait inclure des exemples de code, ou des liens vers de bons, ou de la documentation sur le processus, ou quoi que ce soit.

J'ai vraiment envie de faire cela, mais je suis de frapper un mur géant avec de moche, de la documentation, moche code/code d'exemple/absence de celui-ci. Toute aide/ressources que vous pourriez offrir serait grandement apprécié.

Plus précisément, je voudrais commencer par comment obtenir l'accès à/manipuler le DOM à partir de l'intérieur d'un IE extension.

MODIFIER, encore plus de détails:

Idéalement, je voudrais planter un bouton de barre d'outils qui, lorsqu'il est cliqué, a raté un menu qui contient des liens vers des sites externes. Je tiens également à accéder au DOM et de l'usine de javascript sur la page en fonction de certaines conditions.

Quel est le meilleur moyen de conserver des informations dans un IE extension? Dans Firefox/Chrome/la Plupart des navigateurs modernes, vous utilisez window.localStorage, mais évidemment avec IE8/IE7, ce n'est pas une option. Peut-être un DB SQLite ou tel? Il est ok pour supposer que .NET 4.0 est installé sur l'ordinateur d'un utilisateur?


EDIT:

Je suis fournissant une 500 réputation prime sur ce. Je suis sérieux au sujet d'apprendre à construire un Internet Explorer extension. Je ne veux pas utiliser les Épices IE que je veux en créer une qui est compatible avec IE9. J'ai ajouté le C++ balise à cette question, parce que si c'est mieux d'en construire un en C++, je peux le faire.

229voto

Miguel Angelo Points 12471

L'homme... cela a été beaucoup de travail! J'étais donc curieux de savoir comment le faire, que je l'ai fait moi-même.

Tout d'abord... de crédit n'est pas mienne. C'est une compilation de ce que j'ai trouvé, sur ces sites:

Et bien sûr, je voulais que ma réponse à disposer de toutes les fonctions vous avez demandé:

  • DOM de la traversée de trouver quelque chose;
  • un bouton qui affiche une fenêtre (dans mon cas, pour l'installation)
  • persister la configuration (je vais utiliser regitry pour ça)
  • et enfin exécuter le code javascript.

Je vais le décrire, étape par étape, comment j'ai réussi à le faire fonctionner avec Internet Explorer 8, Windows 7 x64... à noter que je ne pourrais pas tester dans d'autres configurations. J'espère que vous comprenez =)

La création d'un Travail d'Internet Explorer 8 Addon

Je suis à l'aide de Visual Studio 2010, C# 4, .Net Framework 4, de sorte que certains de ces étapes peut être légèrement différent pour vous.

Créé une bibliothèque de classe. J'ai appelé le mien InternetExplorerExtension.

Ajouter ces références pour le projet:

  • Interop.SHDocVw
  • Microsoft.mshtml

Remarque: Ces références peuvent être dans des endroits différents dans chaque ordinateur.

c'est ce que ma section références dans csproj contient:

<Reference Include="Interop.SHDocVw, Version=1.1.0.0, Culture=neutral, PublicKeyToken=90ba9c70f846762e, processorArchitecture=MSIL">
  <SpecificVersion>False</SpecificVersion>
  <EmbedInteropTypes>True</EmbedInteropTypes>
  <HintPath>C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\Interop.SHDocVw.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.mshtml, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
  <EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />

Créer les fichiers suivants:

IEAddon.cs

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.Win32;
using mshtml;
using SHDocVw;

namespace InternetExplorerExtension
{
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [Guid("D40C654D-7C51-4EB3-95B2-1E23905C2A2D")]
    [ProgId("MyBHO.WordHighlighter")]
    public class WordHighlighterBHO : IObjectWithSite, IOleCommandTarget
    {
        const string DefaultTextToHighlight = "browser";

        IWebBrowser2 browser;
        private object site;

        #region Highlight Text
        void OnDocumentComplete(object pDisp, ref object URL)
        {
            try
            {
                // @Eric Stob: Thanks for this hint!
                // This will prevent this method being executed more than once.
                if (pDisp != this.site)
                    return;

                var document2 = browser.Document as IHTMLDocument2;
                var document3 = browser.Document as IHTMLDocument3;

                var window = document2.parentWindow;
                window.execScript(@"function FncAddedByAddon() { alert('Message added by addon.'); }");

                Queue<IHTMLDOMNode> queue = new Queue<IHTMLDOMNode>();
                foreach (IHTMLDOMNode eachChild in document3.childNodes)
                    queue.Enqueue(eachChild);

                while (queue.Count > 0)
                {
                    // replacing desired text with a highlighted version of it
                    var domNode = queue.Dequeue();

                    var textNode = domNode as IHTMLDOMTextNode;
                    if (textNode != null)
                    {
                        if (textNode.data.Contains(TextToHighlight))
                        {
                            var newText = textNode.data.Replace(TextToHighlight, "<span style='background-color: yellow; cursor: hand;' onclick='javascript:FncAddedByAddon()' title='Click to open script based alert window.'>" + TextToHighlight + "</span>");
                            var newNode = document2.createElement("span");
                            newNode.innerHTML = newText;
                            domNode.replaceNode((IHTMLDOMNode)newNode);
                        }
                    }
                    else
                    {
                        // adding children to collection
                        var x = (IHTMLDOMChildrenCollection)(domNode.childNodes);
                        foreach (IHTMLDOMNode eachChild in x)
                        {
                            if (eachChild is mshtml.IHTMLScriptElement)
                                continue;
                            if (eachChild is mshtml.IHTMLStyleElement)
                                continue;

                            queue.Enqueue(eachChild);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        #endregion
        #region Load and Save Data
        static string TextToHighlight = DefaultTextToHighlight;
        public static string RegData = "Software\\MyIEExtension";

        [DllImport("ieframe.dll")]
        public static extern int IEGetWriteableHKCU(ref IntPtr phKey);

        private static void SaveOptions()
        {
            // In IE 7,8,9,(desktop)10 tabs run in Protected Mode
            // which prohibits writes to HKLM, HKCU.
            // Must ask IE for "Writable" registry section pointer
            // which will be something like HKU/S-1-7***/Software/AppDataLow/
            // In "metro" IE 10 mode, tabs run in "Enhanced Protected Mode"
            // where BHOs are not allowed to run, except in edge cases.
            // see http://blogs.msdn.com/b/ieinternals/archive/2012/03/23/understanding-ie10-enhanced-protected-mode-network-security-addons-cookies-metro-desktop.aspx
            IntPtr phKey = new IntPtr();
            var answer = IEGetWriteableHKCU(ref phKey);
            RegistryKey writeable_registry = RegistryKey.FromHandle(
                new Microsoft.Win32.SafeHandles.SafeRegistryHandle(phKey, true)
            );
            RegistryKey registryKey = writeable_registry.OpenSubKey(RegData, true);

            if (registryKey == null)
                registryKey = writeable_registry.CreateSubKey(RegData);
            registryKey.SetValue("Data", TextToHighlight);

            writeable_registry.Close();
        }
        private static void LoadOptions()
        {
            // In IE 7,8,9,(desktop)10 tabs run in Protected Mode
            // which prohibits writes to HKLM, HKCU.
            // Must ask IE for "Writable" registry section pointer
            // which will be something like HKU/S-1-7***/Software/AppDataLow/
            // In "metro" IE 10 mode, tabs run in "Enhanced Protected Mode"
            // where BHOs are not allowed to run, except in edge cases.
            // see http://blogs.msdn.com/b/ieinternals/archive/2012/03/23/understanding-ie10-enhanced-protected-mode-network-security-addons-cookies-metro-desktop.aspx
            IntPtr phKey = new IntPtr();
            var answer = IEGetWriteableHKCU(ref phKey);
            RegistryKey writeable_registry = RegistryKey.FromHandle(
                new Microsoft.Win32.SafeHandles.SafeRegistryHandle(phKey, true)
            );
            RegistryKey registryKey = writeable_registry.OpenSubKey(RegData, true);

            if (registryKey == null)
                registryKey = writeable_registry.CreateSubKey(RegData);
            registryKey.SetValue("Data", TextToHighlight);

            if (registryKey == null)
            {
                TextToHighlight = DefaultTextToHighlight;
            }
            else
            {
                TextToHighlight = (string)registryKey.GetValue("Data");
            }
            writeable_registry.Close();
        }
        #endregion

        [Guid("6D5140C1-7436-11CE-8034-00AA006009FA")]
        [InterfaceType(1)]
        public interface IServiceProvider
        {
            int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject);
        }

        #region Implementation of IObjectWithSite
        int IObjectWithSite.SetSite(object site)
        {
            this.site = site;

            if (site != null)
            {
                LoadOptions();

                var serviceProv = (IServiceProvider)this.site;
                var guidIWebBrowserApp = Marshal.GenerateGuidForType(typeof(IWebBrowserApp)); // new Guid("0002DF05-0000-0000-C000-000000000046");
                var guidIWebBrowser2 = Marshal.GenerateGuidForType(typeof(IWebBrowser2)); // new Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E");
                IntPtr intPtr;
                serviceProv.QueryService(ref guidIWebBrowserApp, ref guidIWebBrowser2, out intPtr);

                browser = (IWebBrowser2)Marshal.GetObjectForIUnknown(intPtr);

                ((DWebBrowserEvents2_Event)browser).DocumentComplete +=
                    new DWebBrowserEvents2_DocumentCompleteEventHandler(this.OnDocumentComplete);
            }
            else
            {
                ((DWebBrowserEvents2_Event)browser).DocumentComplete -=
                    new DWebBrowserEvents2_DocumentCompleteEventHandler(this.OnDocumentComplete);
                browser = null;
            }
            return 0;
        }
        int IObjectWithSite.GetSite(ref Guid guid, out IntPtr ppvSite)
        {
            IntPtr punk = Marshal.GetIUnknownForObject(browser);
            int hr = Marshal.QueryInterface(punk, ref guid, out ppvSite);
            Marshal.Release(punk);
            return hr;
        }
        #endregion
        #region Implementation of IOleCommandTarget
        int IOleCommandTarget.QueryStatus(IntPtr pguidCmdGroup, uint cCmds, ref OLECMD prgCmds, IntPtr pCmdText)
        {
            return 0;
        }
        int IOleCommandTarget.Exec(IntPtr pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
        {
            try
            {
                // Accessing the document from the command-bar.
                var document = browser.Document as IHTMLDocument2;
                var window = document.parentWindow;
                var result = window.execScript(@"alert('You will now be allowed to configure the text to highlight...');");

                var form = new HighlighterOptionsForm();
                form.InputText = TextToHighlight;
                if (form.ShowDialog() != DialogResult.Cancel)
                {
                    TextToHighlight = form.InputText;
                    SaveOptions();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

            return 0;
        }
        #endregion

        #region Registering with regasm
        public static string RegBHO = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Browser Helper Objects";
        public static string RegCmd = "Software\\Microsoft\\Internet Explorer\\Extensions";

        [ComRegisterFunction]
        public static void RegisterBHO(Type type)
        {
            string guid = type.GUID.ToString("B");

            // BHO
            {
                RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(RegBHO, true);
                if (registryKey == null)
                    registryKey = Registry.LocalMachine.CreateSubKey(RegBHO);
                RegistryKey key = registryKey.OpenSubKey(guid);
                if (key == null)
                    key = registryKey.CreateSubKey(guid);
                key.SetValue("Alright", 1);
                registryKey.Close();
                key.Close();
            }

            // Command
            {
                RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(RegCmd, true);
                if (registryKey == null)
                    registryKey = Registry.LocalMachine.CreateSubKey(RegCmd);
                RegistryKey key = registryKey.OpenSubKey(guid);
                if (key == null)
                    key = registryKey.CreateSubKey(guid);
                key.SetValue("ButtonText", "Highlighter options");
                key.SetValue("CLSID", "{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}");
                key.SetValue("ClsidExtension", guid);
                key.SetValue("Icon", "");
                key.SetValue("HotIcon", "");
                key.SetValue("Default Visible", "Yes");
                key.SetValue("MenuText", "&Highlighter options");
                key.SetValue("ToolTip", "Highlighter options");
                //key.SetValue("KeyPath", "no");
                registryKey.Close();
                key.Close();
            }
        }

        [ComUnregisterFunction]
        public static void UnregisterBHO(Type type)
        {
            string guid = type.GUID.ToString("B");
            // BHO
            {
                RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(RegBHO, true);
                if (registryKey != null)
                    registryKey.DeleteSubKey(guid, false);
            }
            // Command
            {
                RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(RegCmd, true);
                if (registryKey != null)
                    registryKey.DeleteSubKey(guid, false);
            }
        }
        #endregion
    }
}

Interop.cs

using System;
using System.Runtime.InteropServices;
namespace InternetExplorerExtension
{
    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("FC4801A3-2BA9-11CF-A229-00AA003D7352")]
    public interface IObjectWithSite
    {
        [PreserveSig]
        int SetSite([MarshalAs(UnmanagedType.IUnknown)]object site);
        [PreserveSig]
        int GetSite(ref Guid guid, [MarshalAs(UnmanagedType.IUnknown)]out IntPtr ppvSite);
    }


    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct OLECMDTEXT
    {
        public uint cmdtextf;
        public uint cwActual;
        public uint cwBuf;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
        public char rgwz;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct OLECMD
    {
        public uint cmdID;
        public uint cmdf;
    }

    [ComImport(), ComVisible(true),
    Guid("B722BCCB-4E68-101B-A2BC-00AA00404770"),
    InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IOleCommandTarget
    {

        [return: MarshalAs(UnmanagedType.I4)]
        [PreserveSig]
        int QueryStatus(
            [In] IntPtr pguidCmdGroup,
            [In, MarshalAs(UnmanagedType.U4)] uint cCmds,
            [In, Out, MarshalAs(UnmanagedType.Struct)] ref OLECMD prgCmds,
            //This parameter must be IntPtr, as it can be null
            [In, Out] IntPtr pCmdText);

        [return: MarshalAs(UnmanagedType.I4)]
        [PreserveSig]
        int Exec(
            //[In] ref Guid pguidCmdGroup,
            //have to be IntPtr, since null values are unacceptable
            //and null is used as default group!
            [In] IntPtr pguidCmdGroup,
            [In, MarshalAs(UnmanagedType.U4)] uint nCmdID,
            [In, MarshalAs(UnmanagedType.U4)] uint nCmdexecopt,
            [In] IntPtr pvaIn,
            [In, Out] IntPtr pvaOut);
    }
}

et, enfin, une forme, que nous allons utiliser pour configurer les options. Dans ce formulaire, placez un TextBox et un Ok Button. Définir la DialogResult du bouton Ok. Placez ce code dans le code du formulaire:

using System.Windows.Forms;
namespace InternetExplorerExtension
{
    public partial class HighlighterOptionsForm : Form
    {
        public HighlighterOptionsForm()
        {
            InitializeComponent();
        }

        public string InputText
        {
            get { return this.textBox1.Text; }
            set { this.textBox1.Text = value; }
        }
    }
}

Dans les propriétés du projet, procédez de la manière suivante:

  • Signer l'assembly avec un forte-clé;
  • Dans l'onglet Débogage, jeu de Démarrer le Programme Externe à l' C:\Program Files (x86)\Internet Explorer\iexplore.exe
  • Dans l'onglet Débogage, définissez les Arguments de Ligne de Commande pour http://msdn.microsoft.com/en-us/library/ms976373.aspx#bho_getintouch
  • Dans la génération des Événements onglet, définissez Après les événements de construction de la ligne de commande :

    "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Outils\x64\gacutil.exe" /f /i", $(TargetDir)$(TargetFileName)"
    
    "C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe" /unregister "$(TargetDir)$(TargetFileName)"
    
    "C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe" "$(TargetDir)$(TargetFileName)"

Attention: comme mon ordinateur est de 64 bits, il est x64 à l'intérieur du chemin de gacutil exécutable sur ma machine qui peut être différente sur le votre.

64bit IE Besoins 64bit-compilé et 64 bits enregistrés BHO. Utilisation 64bit RegAsm.exe (vit habituellement dans C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe)

Comment cet addon fonctionne

Il parcourt tous les DOM arbre, en remplaçant le texte, configuré à l'aide du bouton, par lui-même avec un fond jaune. Si vous cliquez sur le jaunies textes, il appelle une fonction javascript qui a été inséré sur la page dynamiquement. Le mot par défaut est "navigateur", de sorte qu'il corresponde a beaucoup! EDIT: après la modification de la chaîne en surbrillance, vous devez cliquer sur la zone de l'URL et appuyez sur Entrée... F5 ne fonctionne pas, je pense que c'est parce que F5 est considéré comme "la navigation", et on aurait besoin d'écouter pour naviguer de l'événement (peut-être). Je vais essayer de corriger ça plus tard.

Maintenant, il est temps d'y aller. Je suis très fatigué. N'hésitez pas à poser des questions... peut-être que je ne vais pas être en mesure de répondre parce que je suis allez sur un voyage... dans 3 jours je suis de retour, mais je vais essayer de venir ici en attendant.

12voto

Shaish Points 177

Une autre approche cool serait d’aller sur : http://crossrider.com c’est un cadre basé sur JS avec jquery qui vous permet de développer des extensions de navigateurs pour IE, FF et Chrome avec un seul code JS. Fondamentalement, le cadre fait tout le travail de méchant et vous vous retrouvez avec l’écriture de votre code d’applications.

12voto

cloudraven Points 802

L'état pour IE extensions est en fait assez triste. Vous avez le vieux modèle de IE5 Browser Helper Object (ouais, ces infâmes Bho que tout le monde aimait le bloc de retour dans la journée), les barres d'outils et les nouveaux accélérateurs pour IE. Même alors, la compatibilité avec les briser parfois. J'ai utilisé pour maintenir une extension pour IE6, qui rompt avec IE7, donc il y a des choses qui ont changé. Pour la plupart, pour autant que je sais (je n'ai pas toucher Bho en années) vous avez encore besoin de code utilisant des Bibliothèques de Modèle Actif (un peu comme un STL pour Microsoft COM) et bien en tant que tel n'est pour le C++. Vous pourriez faire COM Interop avec C# et de s'en tirer avec faire en C#, mais c'est probablement va être trop dur pour ce qu'il vaut. De toute façon, si vous êtes intéressé dans le codage de votre propre extension pour IE (ce qui est plausible si vous voulez avoir vos extensions disponibles dans tous les principaux navigateurs) voici l'officiel de Microsoft Ressources.

http://msdn.microsoft.com/en-us/library/aa753587(v=vs. 85).aspx

Et pour les accélérateurs qui sont nouveaux dans IE8, vous pouvez consulter celui-ci.

http://msdn.microsoft.com/en-us/library/cc289775(v=vs. 85).aspx

Je suis d'accord la documentation est terrible, et les Api sont tout à fait obsolète. Je espère que cette aide.

EDIT: je suppose que je peux jeter une dernière source d'information ici. Je regardais à travers mes notes de l'époque où je travaillais sur les Bho. Et c'est l'article qui m'a fait commencé avec eux. Il est un peu vieux, mais a une bonne explication de l'ATL interfaces que vous allez utiliser lorsque vous travaillez avec IE Bho (IObjectWithSite par exemple). Je pense que c'est assez bien expliqué et m'a beaucoup aidé. http://msdn.microsoft.com/en-us/library/bb250436.aspx J'ai aussi vérifié l'exemple que GregC posté. Il fonctionne avec au moins IE8, et il est compatible avec VS 2010, donc si vous voulez faire du C#, vous pouvez commencer là et de prendre un coup d'oeil à Jon Skeet du Livre. (C# dans la Profondeur de la 2e édition) Chapitre 13 a une bonne quantité d'informations sur les nouvelles fonctionnalités de C# 4 que vous pouvez utiliser pour faire de l'interaction avec COM plus agréable. (Je voudrais encore vous recommandons de vous faire votre addin en C++)

6voto

En développement C# Bho est une douleur dans le cul. Il implique beaucoup de icky COM code et de p/invoke appels.

J'ai pratiquement fini C# BHO ici, vous êtes libre d'utiliser la source pour tout ce que vous voulez. Je dis "presque", parce que je n'ai jamais trouver comment enregistrer appdata sous IE en Mode Protégé.

4voto

Lynn Crumbling Points 4264

J'ai travaillé avec IE contrôle webbrowser depuis des années, et au cours d'eux, un nom revient encore et encore utiles messages: Igor Tandetnik

Si j'étais le développement d'une extension, je cible un BHO, et commencer à googler pour:

BHO Igor Tandetnik

OU

Browser Helper Object Igor Tandetnik

Ses offres sont souvent très détaillées, et il sait de quoi il parle.

Vous allez trouver vous-même à vos oreilles dans la COM et de l'ATL de programmation. Pour un exemple de procédure pas à pas, découvrez: http://msdn.microsoft.com/en-us/library/ms976373.aspx

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X