Comment commencer à développer des extensions Internet Explorer?

Quelqu’un at-il de l’expérience avec / dans le développement d’extensions IE pouvant partager ses connaissances? Cela inclurait des exemples de code, ou des liens vers de bons, ou de la documentation sur le processus, ou n’importe quoi.

Je veux vraiment le faire, mais je frappe un mur géant avec de la documentation moche, du code de code / code exemple / manque de documentation. Toute aide / ressources que vous pourriez offrir serait grandement appréciée.

Plus précisément, je voudrais commencer par avoir access à / manipuler le DOM à partir d’une extension IE.

EDIT, encore plus de détails:

Dans l’idéal, je souhaiterais placer un bouton de la barre d’outils qui, une fois cliqué, affiche un menu contenant des liens vers des sites externes. Je voudrais aussi accéder au DOM et à JavaScript sur la page en fonction de certaines conditions.

Quelle est la meilleure façon de conserver les informations dans une extension IE? 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 une firebase database SQLite ou autre? Il est correct de supposer que .NET 4.0 sera installé sur l’ordinateur de l’utilisateur?

Je ne veux pas utiliser Spice IE car je veux en créer un qui soit également compatible avec IE9. J’ai également ajouté le tag C ++ à cette question, car s’il est préférable d’en créer un en C ++, je peux le faire.

Man … ça fait beaucoup de travail! J’étais tellement curieux de savoir comment faire cela, que je l’ai fait moi-même.

Tout d’abord … le crédit n’est pas tout à moi. Ceci est une compilation de ce que j’ai trouvé sur ces sites:

  • Article CodeProject , comment faire un BHO;
  • 15 secondes, mais ce n’était pas 15 secondes, il a fallu environ 7 heures;
  • Le didacticiel de Microsoft m’a aidé à append le bouton de commande.
  • Et ce sujet social.msdn , qui m’a aidé à comprendre que l’assemblage doit être dans le GAC.
  • Cet article de blog MSDN récent contient un exemple complet
  • de nombreux autres sites, dans le processus de découverte …

Et bien sûr, je voulais que ma réponse ait les fonctionnalités que vous avez demandées:

  • Traversée DOM pour trouver quelque chose;
  • un bouton qui affiche une fenêtre (dans mon cas, pour configurer)
  • persister la configuration (je vais utiliser regitry pour cela)
  • et enfin exécuter javascript.

Je vais le décrire étape par étape, comment j’ai réussi à le faire en utilisant Internet Explorer 8 , sous Windows 7 x64 … notez que je ne pouvais pas tester dans d’autres configurations. J’espère que vous comprenez =)

Créer un complément Internet Explorer 8 fonctionnel

J’utilise Visual Studio 2010 , C # 4 , .Net Framework 4 , certaines de ces étapes peuvent donc être légèrement différentes pour vous.

Création d’une bibliothèque de classes. J’ai appelé le mien InternetExplorerExtension .

Ajoutez ces références au projet:

  • Interop.SHDocVw
  • Microsoft.mshtml

Remarque: ces références peuvent se trouver à différents endroits dans chaque ordinateur.

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

  False True C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\Interop.SHDocVw.dll    True       

Créez 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 ssortingng 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 queue = new Queue(); 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, "" + TextToHighlight + ""); 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 ssortingng TextToHighlight = DefaultTextToHighlight; public static ssortingng 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 = (ssortingng)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 ssortingng RegBHO = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Browser Helper Objects"; public static ssortingng RegCmd = "Software\\Microsoft\\Internet Explorer\\Extensions"; [ComRegisterFunction] public static void RegisterBHO(Type type) { ssortingng guid = type.GUID.ToSsortingng("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) { ssortingng guid = type.GUID.ToSsortingng("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"), InterfaceTypeAtsortingbute(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 un formulaire que nous utiliserons pour configurer les options. Dans cette forme, placez un TextBox et un Button Ok. Définissez le DialogResult du bouton sur Ok . Placez ce code dans le code du formulaire:

 using System.Windows.Forms; namespace InternetExplorerExtension { public partial class HighlighterOptionsForm : Form { public HighlighterOptionsForm() { InitializeComponent(); } public ssortingng InputText { get { return this.textBox1.Text; } set { this.textBox1.Text = value; } } } } 

Dans les propriétés du projet, procédez comme suit:

  • Signez l’assemblage avec une clé forte;
  • Dans l’onglet Débogage, définissez Démarrer le programme externe sur C:\Program Files (x86)\Internet Explorer\iexplore.exe
  • Dans l’onglet Débogage, définissez les arguments de ligne de commande sur http://msdn.microsoft.com/en-us/library/ms976373.aspx#bho_getintouch
  • Dans l’onglet Créer des événements, définissez la ligne de commande des événements de post-génération sur:

      "C: \ Program Files (x86) \ Kit de développement Microsoft \ 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 x64, il existe un x64 spécifique dans le chemin de l’exécutable de gacutil sur ma machine, qui peut être différent sur le vôtre.

IE 64 bits Nécessite un BHO compilé 64 bits et enregistré 64 bits. Utilisez 64bit RegAsm.exe (généralement situé dans C: \ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319 \ RegAsm.exe)

Comment cet addon fonctionne

Il traverse tout arbre DOM, en remplaçant le texte, configuré à l’aide du bouton, par lui-même avec un fond jaune. Si vous cliquez sur les textes jaunis, il appelle une fonction javascript qui a été insérée dynamicment sur la page. Le mot par défaut est “browser”, de sorte qu’il correspond à beaucoup d’entre eux! EDIT: après avoir modifié la chaîne à mettre en évidence, vous devez cliquer sur la case URL et appuyer sur Entrée … F5 ne fonctionnera pas, je pense que c’est parce que F5 est considéré comme “navigation”, et qu’il faudra l’écouter pour naviguer (peut être). Je vais essayer de résoudre ce problème plus tard.

Maintenant, il est temps de partir. Je suis très fatigué. N’hésitez pas à poser des questions … peut-être que je ne serai pas en mesure de répondre puisque je pars en voyage … dans 3 jours je suis de retour, mais je vais essayer de venir ici entre temps.

Une autre approche intéressante serait de vérifier:

http://www.crossrider.org

C’est un framework basé sur JS avec jquery qui vous permet de développer des extensions de navigateur pour IE, FF et Chrome en utilisant un seul code JS commun. Fondamentalement, le framework fait tout le travail désagréable et il vous rest à écrire votre code d’application.

L’état des extensions IE est en réalité assez sortingste. Vous avez l’ancien modèle d’IE5 Browser Helper Object (oui, ces sortingstement célèbres BHO que tout le monde aimait bloquer dans la journée), les barres d’outils et les nouveaux accélérateurs pour IE. Même alors, la compatibilité sera parfois rompue. J’avais l’habitude de conserver une extension pour IE6 qui rompait avec IE7, donc il y a des choses qui ont changé. Pour la plupart, pour autant que je sache (je n’ai pas touché aux BHO depuis des années), vous devez toujours les coder à l’aide d’Active Template Libraries (un peu comme une STL pour Microsoft COM) et c’est uniquement pour C ++. Vous pouvez faire COM Interop avec C # et vous en sortir en C # mais ça va probablement être trop dur pour ce que ça vaut. Quoi qu’il en soit, si vous souhaitez coder votre propre extension pour IE (ce qui est plausible si vous souhaitez que vos extensions soient disponibles dans tous les principaux navigateurs), voici les ressources officielles de Microsoft.

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 vérifier celui-ci.

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

Je suis d’accord que la documentation est terrible, et les API sont assez obsolètes. J’espère toujours que cela aide.

EDIT: Je suppose que je peux jeter une dernière source d’informations ici. Je parcourais mes notes quand je travaillais sur les BHO. Et c’est l’article qui m’a permis de commencer avec eux. C’est un peu ancien, mais il a une bonne explication des interfaces ATL que vous utiliserez lorsque vous travaillerez avec les BHO IE (IObjectWithSite par exemple). Je pense que c’est assez bien expliqué et m’a beaucoup aidé à l’époque. http://msdn.microsoft.com/en-us/library/bb250436.aspx J’ai également vérifié l’exemple publié par GregC. Il fonctionne avec au moins IE8, et il est compatible avec VS 2010, donc si vous voulez faire C #, vous pouvez vous y lancer et jeter un œil au Livre de Jon Skeet. (C # in Depth 2nd edition) Le chapitre 13 contient beaucoup d’informations sur les nouvelles fonctionnalités de C # 4 que vous pouvez utiliser pour rendre l’interaction avec COM plus agréable. (Je vous recommanderais quand même de faire votre addin en C ++)

Développer les BHOs ​​C # est une expérience difficile. Il implique beaucoup de code COM icky et d’appels p / invoke.

J’ai un C # BHO presque fini ici , que vous êtes libre d’utiliser la source pour ce que vous voulez. Je dis “principalement” , parce que je n’ai jamais compris comment enregistrer appdata sous IE Protected Mode .

Cela fait des années que je travaille avec le contrôle de navigateur Web d’IE, et au fil des années, un nom revient encore et encore avec des articles utiles: Igor Tandetnik

Si je développais une extension, je ciblerais un BHO et commencerais à chercher:

BHO Igor Tandetnik

OU

Objet d’assistance du navigateur Igor Tandetnik

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

Vous allez vous retrouver à vos oreilles dans la programmation COM et ATL. Pour un exemple de procédure pas à pas, consultez: http://msdn.microsoft.com/en-us/library/ms976373.aspx

Je suis d’accord avec Robert Harvey, les fonctionnalités de C # 4.0 améliorent l’interopérabilité COM. Voici un peu d’ancien code C #, qui a désespérément besoin d’une ré-écriture.

http://www.codeproject.com/KB/cs/Attach_BHO_with_C_.aspx

Ceci est une tentative de simplifier les choses en évitant ATL et en allant avec Spartan COM:

C ++ et COM pour faire avancer les BHO

Si vous n’essayez pas de réinventer la roue, vous pouvez essayer Add In Express pour IE . J’ai utilisé le produit pour les choses VSTO , et c’est plutôt bien. Ils ont également un forum utile et un support rapide.

C’est évidemment résolu, mais pour les autres utilisateurs, je recommanderais le framework SpicIE . J’ai créé ma propre extension en fonction de cela. Il ne prend en charge que Internet Explorer 7/8, mais je l’ai testé sur Internet Explorer 6-10 (de Windows XP à Windows 8 Consumer Preview) et cela fonctionne bien . Malheureusement, il y avait des bogues dans la dernière version, alors j’ai dû les réparer et faire ma propre version: http://archive.msdn.microsoft.com/SpicIE/Thread/View.aspx?ThreadId=5251

Je vous suggère chaleureusement ce post de Pavel Zolnikov publié en 2002!

http://www.codeproject.com/Articles/2219/Extending-Explorer-with-Band-Objects-using-NET-and

Il est basé sur l’utilisation d’objects Band et est compilé à l’aide de .Net 2.0. Le code source est fourni et s’ouvre et se comstack bien avec Visual Studio 2013. Comme vous le lirez sur les commentaires, il fonctionne parfaitement pour IE 11 et Windows 7 et Windows 10. Il fonctionnait parfaitement pour Windows 7 + SP1 et IE 11 Profitez-en!

entrer la description de l'image ici

Dans l’onglet Créer des événements, définissez la ligne de commande des événements post-génération sur: (x64) est répertorié ci-dessous.

 "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\x64\gacutil.exe" /if "$(TargetDir)$(TargetFileName)" "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe" /u "$(TargetDir)$(TargetFileName)" "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe" "$(TargetDir)$(TargetFileName)" 

Je veux l’onglet Créer des événements, définir la ligne de commande des événements post-construction sur (système d’exploitation 32 bits)

 "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\gacutil.exe" /if "$(TargetDir)$(TargetFileName)" "C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe" /u "$(TargetDir)$(TargetFileName)" "C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe" "$(TargetDir)$(TargetFileName)"