[MISE À JOUR] Je mets à jour cette réponse pour fonctionner avec Internet Explorer 11 , dans Windows 10 x64 avec la communauté Visual Studio 2017 . La version précédente de cette réponse (pour Internet Explorer 8, dans Windows 7 x64 et Visual Studio 2010) se trouve au bas de cette réponse.
Création d'un module complémentaire Internet Explorer 11 fonctionnel
J'utilise Visual Studio 2017 Community , C # , .Net Framework 4.6.1 , donc certaines de ces étapes peuvent être légèrement différentes pour vous.
Vous devez ouvrir Visual Studio en tant qu'administrateur pour créer la solution, afin que le script post-génération puisse enregistrer le BHO (nécessite un accès au registre).
Commencez par créer une bibliothèque de classes. J'ai appelé le mien InternetExplorerExtension .
Ajoutez ces références au projet:
- Interop.SHDocVw: onglet COM / recherche
"Microsoft Internet Controls"
- Microsoft.mshtml: onglet Assemblages / recherche
"Microsoft.mshtml"
Remarque: MSHTML n'était pas enregistré dans mon système, même si je pouvais le trouver dans la fenêtre Ajouter une référence. Cela a provoqué une erreur lors de la construction:
Impossible de trouver l'assembly d'encapsuleur pour la bibliothèque de types "MSHTML"
Le correctif peut être trouvé à http://techninotes.blogspot.com/2016/08/fixing-cannot-find-wrapper-assembly-for.html
Ou, vous pouvez exécuter ce script de commandes:
"%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat"
cd "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\Common7\IDE\PublicAssemblies"
regasm Microsoft.mshtml.dll
gacutil /i Microsoft.mshtml.dll
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 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 was used to prevent this method being executed more than once in IE8... but now it seems to not work anymore.
//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 un formulaire, que nous utiliserons pour configurer les options. Dans ce formulaire, placez un TextBox
et un Ok Button
. Réglez 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 string 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 touche forte;
- Dans l'onglet Déboguer, définissez Démarrer le programme externe sur
C:\Program Files (x86)\Internet Explorer\iexplore.exe
- Dans l'onglet Déboguer, définissez Arguments de ligne de commande sur
http://msdn.microsoft.com/en-us/library/ms976373.aspx#bho_getintouch
Dans l'onglet Générer des événements, définissez la ligne de commande des événements post-génération sur:
"% ProgramFiles (x86)% \ Microsoft SDKs \ Windows \ v10.0A \ bin \ NETFX 4.6.1 Tools \ gacutil.exe" / f / i "$ (TargetDir) $ (TargetFileName)"
"% windir% \ Microsoft.NET \ Framework \ v4.0.30319 \ RegAsm.exe" / désinscrire "$ (TargetDir) $ (TargetFileName)"
"% windir% \ Microsoft.NET \ Framework \ v4.0.30319 \ RegAsm.exe" "$ (TargetDir) $ (TargetFileName)"
Attention: même si mon ordinateur est x64, j'ai utilisé le chemin du non-x64 gacutil.exe
et cela a fonctionné ... celui spécifique pour x64 est à:
C: \ Program Files (x86) \ Microsoft SDKs \ Windows \ v10.0A \ bin \ NETFX 4.6.1 Tools \ x64 \ gacutil.exe
IE 64 bits a besoin de BHO compilé en 64 bits et enregistré en 64 bits. Bien que je ne pouvais que déboguer en utilisant 32 bits IE11, l'extension enregistrée 32 bits fonctionnait également en exécutant 64 bits IE11.
Cette réponse semble avoir quelques informations supplémentaires à ce sujet: https://stackoverflow.com/a/23004613/195417
Si vous en avez besoin, vous pouvez utiliser le regasm 64 bits:
% windir% \ Microsoft.NET \ Framework 64 \ v4.0.30319 \ RegAsm.exe
Comment fonctionne ce module complémentaire
Je n'ai pas changé le comportement du module complémentaire ... jetez un œil à la section IE8 ci-dessous pour la description.
## Réponse précédente pour IE8
Mec ... ça a été 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. Voici une compilation de ce que j'ai trouvé sur ces sites:
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)
- conserver la configuration (j'utiliserai le registre pour cela)
- et enfin exécuter javascript.
Je vais le décrire étape par étape, comment j'ai réussi à le faire en travaillant avec Internet Explorer 8 , sous Windows 7 x64 ... notez que je n'ai pas pu tester dans d'autres configurations. J'espère que vous comprenez =)
Création d'un module complémentaire Internet Explorer 8 fonctionnel
J'utilise Visual Studio 2010 , C # 4 , .Net Framework 4 , donc certaines de ces étapes peuvent ê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 sur chaque ordinateur.
voici ce que contient ma section références dans csproj:
<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éez les fichiers de la même manière que les fichiers IE11 mis à jour.
IEAddon.cs
Vous pouvez décommenter les lignes suivantes de la version IE11:
...
// @Eric Stob: Thanks for this hint!
// This was used to prevent this method being executed more than once in IE8... but now it seems to not work anymore.
if (pDisp != this.site)
return;
...
Interop.cs
Identique à la version IE11.
et enfin un formulaire, que nous utiliserons pour configurer les options. Dans ce formulaire, placez un TextBox
et un Ok Button
. Réglez DialogResult du bouton sur Ok . Le code est le même pour l'addon IE11.
Dans les propriétés du projet, procédez comme suit:
- Signez l'assemblage avec une touche forte;
- Dans l'onglet Déboguer, définissez Démarrer le programme externe sur
C:\Program Files (x86)\Internet Explorer\iexplore.exe
- Dans l'onglet Déboguer, définissez Arguments de ligne de commande sur
http://msdn.microsoft.com/en-us/library/ms976373.aspx#bho_getintouch
Dans l'onglet Générer des événements, définissez la ligne de commande des événements post-génération sur:
"C: \ Program Files (x86) \ Microsoft SDKs \ Windows \ v7.0A \ Bin \ NETFX 4.0 Tools \ x64 \ gacutil.exe" / f / i "$ (TargetDir) $ (TargetFileName)"
"C: \ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ RegAsm.exe" / désinscrire "$ (TargetDir) $ (TargetFileName)"
"C: \ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ RegAsm.exe" "$ (TargetDir) $ (TargetFileName)"
Attention: comme mon ordinateur est x64, il y a un x64 spécifique à l'intérieur du chemin de l'exécutable gacutil sur ma machine qui peut être différent sur le vôtre.
IE 64 bits a besoin de BHO compilé en 64 bits et enregistré en 64 bits. Utilisez RegAsm.exe 64 bits (réside généralement dans C: \ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319 \ RegAsm.exe)
Comment fonctionne ce module complémentaire
Il parcourt tout l'arborescence DOM, remplaçant le texte, configuré à l'aide du bouton, par lui-même avec un fond jaune. Si vous cliquez sur les textes jaunis, cela appelle une fonction javascript qui a été insérée dynamiquement sur la page. Le mot par défaut est «navigateur», de sorte qu'il correspond à beaucoup d'entre eux!
EDIT: après avoir changé la chaîne à mettre en surbrillance, 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 il faudrait l'écouter pour naviguer l'événement (peut être). J'essaierai 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 pourrai pas répondre puisque je pars en voyage ... dans 3 jours je serai de retour, mais j'essaierai de venir ici en attendant.