[AKTUALIZACJA] Aktualizuję tę odpowiedź do pracy z programem Internet Explorer 11 w systemie Windows 10 x64 ze społecznością Visual Studio 2017 . Poprzednia wersja tej odpowiedzi (dla Internet Explorera 8, Windows 7 x64 i Visual Studio 2010) znajduje się na dole tej odpowiedzi.
Tworzenie działającego dodatku do programu Internet Explorer 11
Korzystam z Visual Studio 2017 Community , C # , .Net Framework 4.6.1 , więc niektóre z tych kroków mogą być nieco inne dla Ciebie.
Musisz otworzyć Visual Studio jako Administrator, aby zbudować rozwiązanie, aby skrypt po kompilacji mógł zarejestrować BHO (wymaga dostępu do rejestru).
Zacznij od utworzenia biblioteki klas. Zadzwoniłem do mojego InternetExplorerExtension .
Dodaj te odniesienia do projektu:
- Interop.SHDocVw: karta COM / wyszukaj
"Microsoft Internet Controls"
- Microsoft.mshtml: karta Zespoły / wyszukaj
"Microsoft.mshtml"
Uwaga: Jakoś MSHTML nie został zarejestrowany w moim systemie, chociaż mogłem znaleźć w oknie Dodaj odniesienie. Spowodowało to błąd podczas budowania:
Nie można znaleźć zespołu opakowania dla biblioteki typów „MSHTML”
Poprawka można znaleźć na stronie http://techninotes.blogspot.com/2016/08/fixing-cannot-find-wrapper-assembly-for.html
Lub możesz uruchomić ten skrypt wsadowy:
"%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
Utwórz następujące pliki:
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);
}
}
i wreszcie formularz, którego użyjemy do skonfigurowania opcji. W tej formie umieść a TextBox
i OK Button
. Ustaw DialogResult przycisku na Ok . Umieść ten kod w kodzie formularza:
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; }
}
}
}
We właściwościach projektu wykonaj następujące czynności:
- Podpisz zestaw silnym kluczem;
- Na karcie Debugowanie ustaw opcję Uruchom program zewnętrzny na
C:\Program Files (x86)\Internet Explorer\iexplore.exe
- Na karcie Debugowanie ustaw Argumenty wiersza polecenia na
http://msdn.microsoft.com/en-us/library/ms976373.aspx#bho_getintouch
Na karcie Zdarzenia kompilacji ustaw wiersz polecenia Zdarzenia po kompilacji na:
„% 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” / wyrejestruj „$ (TargetDir) $ (TargetFileName)”
„% windir% \ Microsoft.NET \ Framework \ v4.0.30319 \ RegAsm.exe” „$ (TargetDir) $ (TargetFileName)”
Uwaga: mimo że mój komputer to x64, użyłem ścieżki innej niż x64 gacutil.exe
i zadziałało ... ta specyficzna dla x64 jest na:
C: \ Program Files (x86) \ Microsoft SDKs \ Windows \ v10.0A \ bin \ NETFX 4.6.1 Tools \ x64 \ gacutil.exe
64bit IE Potrzebuje skompilowanego i zarejestrowanego 64-bitowego BHO. Chociaż mogłem debugować tylko przy użyciu 32-bitowego IE11, zarejestrowane rozszerzenie 32-bitowe również działało, uruchamiając 64-bitowy IE11.
Ta odpowiedź zawiera dodatkowe informacje na ten temat: https://stackoverflow.com/a/23004613/195417
Jeśli potrzebujesz, możesz użyć 64-bitowego regasmu:
% windir% \ Microsoft.NET \ Framework 64 \ v4.0.30319 \ RegAsm.exe
Jak działa ten dodatek
Nie zmieniłem zachowania dodatku ... spójrz na opis poniżej sekcji IE8.
## Poprzednia odpowiedź dla IE8
Człowieku ... to było dużo pracy! Byłem tak ciekawy, jak to zrobić, że sam to zrobiłem.
Po pierwsze ... kredyt nie jest cały mój. To jest kompilacja tego, co znalazłem na tych stronach:
I oczywiście chciałem, aby moja odpowiedź zawierała funkcje, o które prosiłeś:
- Przejście DOM, aby znaleźć coś;
- przycisk, który pokazuje okno (w moim przypadku do konfiguracji)
- zachowaj konfigurację (użyję do tego rejestru)
- i wreszcie uruchom javascript.
Opiszę to krok po kroku, jak udało mi się to zrobić przy pomocy Internet Explorera 8 , w Windows 7 x64 ... zauważ, że nie mogłem testować w innych konfiguracjach. Mam nadzieję, że rozumiesz =)
Tworzenie działającego dodatku do programu Internet Explorer 8
Używam Visual Studio 2010 , C # 4 , .Net Framework 4 , więc niektóre z tych kroków mogą być nieco inne dla Ciebie.
Utworzono bibliotekę klas. Zadzwoniłem do mojego InternetExplorerExtension .
Dodaj te odniesienia do projektu:
- Interop.SHDocVw
- Microsoft.mshtml
Uwaga: te odniesienia mogą znajdować się w różnych miejscach na każdym komputerze.
oto, co zawiera moja sekcja referencji w 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" />
Utwórz pliki w taki sam sposób, jak zaktualizowane pliki IE11.
IEAddon.cs
Możesz odkomentować następujące wiersze z wersji 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
Taki sam jak wersja IE11.
i wreszcie formularz, którego użyjemy do skonfigurowania opcji. W tej formie umieść a TextBox
i OK Button
. Ustaw DialogResult przycisku na Ok . Kod jest taki sam dla dodatku IE11.
We właściwościach projektu wykonaj następujące czynności:
- Podpisz zestaw silnym kluczem;
- Na karcie Debugowanie ustaw opcję Uruchom program zewnętrzny na
C:\Program Files (x86)\Internet Explorer\iexplore.exe
- Na karcie Debugowanie ustaw Argumenty wiersza polecenia na
http://msdn.microsoft.com/en-us/library/ms976373.aspx#bho_getintouch
Na karcie Zdarzenia kompilacji ustaw wiersz polecenia Zdarzenia po kompilacji na:
„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” / wyrejestruj „$ (TargetDir) $ (TargetFileName)”
„C: \ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ RegAsm.exe” „$ (TargetDir) $ (TargetFileName)”
Uwaga: ponieważ moim komputerem jest x64, na ścieżce programu gacutil na moim komputerze znajduje się określony x64, który może być inny na twoim.
64bit IE Potrzebuje skompilowanego i zarejestrowanego 64-bitowego BHO. Użyj 64-bitowego RegAsm.exe (zwykle mieszka w C: \ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319 \ RegAsm.exe)
Jak działa ten dodatek
Przechodzi przez całe drzewo DOM, zastępując tekst, skonfigurowany za pomocą przycisku, sam z żółtym tłem. Kliknięcie pożółkłych tekstów wywołuje funkcję javascript, która została dynamicznie wstawiona na stronie. Domyślnym słowem jest „przeglądarka”, więc pasuje do wielu z nich!
EDYCJA: po zmianie ciągu, który ma być podświetlony, musisz kliknąć pole adresu URL i nacisnąć Enter ... F5 nie będzie działać, myślę, że dzieje się tak, ponieważ F5 jest uważane za „nawigację” i wymagałoby odsłuchania zdarzenia nawigacyjnego (może). Spróbuję to naprawić później.
Czas już iść. Jestem bardzo zmęczony. Zapraszam do zadawania pytań ... być może nie będę w stanie odpowiedzieć, ponieważ jadę na wycieczkę ... za 3 dni wrócę, ale w międzyczasie postaram się tu przyjechać.