[ОНОВЛЕННЯ] Цю відповідь я оновлюю для роботи з Internet Explorer 11 , в Windows 10 x64 із спільнотою Visual Studio 2017 . Попередня версія цієї відповіді (для Internet Explorer 8, в Windows 7 x64 та Visual Studio 2010) знаходиться внизу цієї відповіді.
Створення робочого додатка Internet Explorer 11
Я використовую Visual Studio 2017 Community , C # , .Net Framework 4.6.1 , тому деякі з цих кроків можуть бути для вас дещо іншими.
Для створення рішення потрібно відкрити Visual Studio як адміністратор, щоб сценарій після складання міг зареєструвати BHO (потребує доступу до реєстру).
Почніть зі створення бібліотеки класів. Я зателефонував до свого InternetExplorerExtension .
Додайте ці посилання до проекту:
- Interop.SHDocVw: вкладка COM / пошук
"Microsoft Internet Controls"
- Microsoft.mshtml: вкладка Асамблеї / пошук
"Microsoft.mshtml"
Примітка: Якось MSHTML не був зареєстрований у моїй системі, хоча я міг знайти його у вікні Додати довідку. Це спричинило помилку під час створення:
Неможливо знайти збірку обгортки для бібліотеки типів "MSHTML"
Виправлення можна знайти на веб-сайті http://techninotes.blogspot.com/2016/08/fixing-cannot-find-wrapper-assembly-for.html
Або ви можете запустити цей пакетний сценарій:
"%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
Створіть такі файли:
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);
}
}
і нарешті форма, яку ми будемо використовувати для налаштування параметрів. У цій формі поставте а TextBox
та Ок Button
. Встановіть кнопку DialogResult на Ок . Розмістіть цей код у коді форми:
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; }
}
}
}
У властивостях проекту виконайте наступне:
- Підпишіть збірку за допомогою кнопок;
- На вкладці «Налагодження» встановіть « Запустити зовнішню програму» на
C:\Program Files (x86)\Internet Explorer\iexplore.exe
- На вкладці Налагодження встановіть Аргументи командного рядка на
http://msdn.microsoft.com/en-us/library/ms976373.aspx#bho_getintouch
На вкладці "Події збирання" встановіть командний рядок " Події після збирання" на:
"% ProgramFiles (x86)% \ Microsoft SDKs \ Windows \ v10.0A \ bin \ NETFX 4.6.1 Інструменти \ gacutil.exe" / f / i "$ (TargetDir) $ (TargetFileName)"
"% windir% \ Microsoft.NET \ Framework \ v4.0.30319 \ RegAsm.exe" / скасувати реєстрацію "$ (TargetDir) $ (TargetFileName)"
"% windir% \ Microsoft.NET \ Framework \ v4.0.30319 \ RegAsm.exe" "$ (TargetDir) $ (TargetFileName)"
Увага: незважаючи на те, що мій комп'ютер x64, я використовував шлях не-x64, gacutil.exe
і він працював ... специфічний для x64:
C: \ Програмні файли (x86) \ Microsoft SDKs \ Windows \ v10.0A \ bin \ NETFX 4.6.1 Інструменти \ x64 \ gacutil.exe
64-бітний IE потребує 64-бітового компільованого та 64-бітового реєстрації BHO. Хоча я міг налагоджувати лише за допомогою 32-бітового IE11, 32-бітове зареєстроване розширення також працювало за допомогою 64-бітного IE11.
Здається, у цій відповіді є додаткова інформація про це: https://stackoverflow.com/a/23004613/195417
Якщо вам потрібно, ви можете використовувати 64-бітовий регазм:
% windir% \ Microsoft.NET \ Framework 64 \ v4.0.30319 \ RegAsm.exe
Як працює ця надбудова
Я не змінив поведінку надбудови ... подивіться нижче на опис IE8.
Попередній відповідь для IE8
Людина ... над цим було багато роботи! Мені було так цікаво, як це зробити, що я це зробив сам.
Перш за все ... кредит - це не все моє. Це збірка того, що я знайшов на цих сайтах:
І звичайно, я хотів, щоб моя відповідь мала функції, про які ви питали:
- Обхід DOM, щоб знайти щось;
- кнопка, що показує вікно (у моєму випадку для налаштування)
- зберегти конфігурацію (я буду використовувати для цього реєстр)
- і нарешті виконати JavaScript.
Я опишу це крок за кроком, як мені вдалося це зробити, працюючи з Internet Explorer 8 , в Windows 7 x64 ... зауважте, що я не міг протестувати в інших конфігураціях. Сподіваюся, ви зрозуміли =)
Створення робочого додатка Internet Explorer 8
Я використовую Visual Studio 2010 , C # 4 , .Net Framework 4 , тому деякі з цих кроків можуть бути для вас дещо іншими.
Створили бібліотеку класів. Я зателефонував до свого InternetExplorerExtension .
Додайте ці посилання до проекту:
- Interop.SHDocVw
- Microsoft.mshtml
Примітка. Ці посилання можуть бути в різних місцях на кожному комп’ютері.
ось що містить мій посилання у розділі 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" />
Створіть файли так само, як і оновлені файли IE11.
IEAddon.cs
Ви можете коментувати наступні рядки з версії 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
Те саме, що і версія IE11.
і нарешті форма, яку ми будемо використовувати для налаштування параметрів. У цій формі поставте а TextBox
та Ок Button
. Встановіть кнопку DialogResult на Ок . Код такий самий для IE11 аддона.
У властивостях проекту виконайте наступне:
- Підпишіть збірку за допомогою кнопок;
- На вкладці «Налагодження» встановіть « Запустити зовнішню програму» на
C:\Program Files (x86)\Internet Explorer\iexplore.exe
- На вкладці Налагодження встановіть Аргументи командного рядка на
http://msdn.microsoft.com/en-us/library/ms976373.aspx#bho_getintouch
На вкладці "Події збирання" встановіть командний рядок " Події після збирання" на:
"C: \ Файли програми (x86) \ Microsoft SDKs \ Windows \ v7.0A \ Bin \ NETFX 4.0 Інструменти \ x64 \ gacutil.exe" / f / i "$ (TargetDir) $ (TargetFileName)"
"C: \ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ RegAsm.exe" / скасувати реєстрацію "$ (TargetDir) $ (TargetFileName)"
"C: \ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ RegAsm.exe" "$ (TargetDir) $ (TargetFileName)"
Увага: оскільки мій комп’ютер x64, всередині шляху моєї машини, що виконується, є специфічний x64, який може відрізнятись від вашого.
64-бітний IE потребує 64-бітового компільованого та 64-бітового реєстрації BHO. Використовуйте 64-бітний RegAsm.exe (зазвичай живе в C: \ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319 \ RegAsm.exe)
Як працює ця надбудова
Він обходить все дерево DOM, замінюючи текст, налаштований за допомогою кнопки, самим жовтим фоном. Якщо натиснути на пожовклі тексти, він викликає функцію javascript, яка була вставлена на сторінку динамічно. За замовчуванням слово "браузер", так що воно відповідає багатьом!
EDIT: після зміни рядка для виділення потрібно натиснути поле URL-адреси та натиснути Enter ... F5 не працюватиме, я думаю, що це тому, що F5 вважається "навігацією", і це вимагатиме прослуховування події навігації (може бути). Спробую це виправити пізніше.
Тепер пора йти. Я дуже втомився. Не соромтеся задавати питання ... можливо, я не зможу відповісти, оскільки їду в подорож ... через 3 дні я повернувся, але тим часом спробую приїхати сюди.