[อัพเดท] ฉันกำลังอัปเดตคำตอบนี้เพื่อทำงานกับInternet Explorer 11ในWindows 10 x64กับชุมชน Visual Studio 2017ชุมชน รุ่นก่อนหน้าของคำตอบนี้ (สำหรับ Internet Explorer 8 ใน Windows 7 x64 และ Visual Studio 2010) อยู่ที่ด้านล่างของคำตอบนี้
การสร้าง Add-on ที่ทำงานกับ Internet Explorer 11
ฉันใช้ชุมชน Visual Studio 2017 , 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
- ในแท็บ Debug ตั้งอาร์กิวเมนต์บรรทัดคำสั่งเป็น
http://msdn.microsoft.com/en-us/library/ms976373.aspx#bho_getintouch
ในแท็บ Build Events ให้ตั้งค่าบรรทัดคำสั่ง Post-build eventsเป็น:
"% 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" / ถอนการลงทะเบียน "$ (TargetDir) $ (TargetFileName)"
"% windir% \ Microsoft.NET \ Framework \ v4.0.30319 \ RegAsm.exe" "$ (TargetDir) $ (TargetFileName)"
ข้อควรระวัง:แม้ว่าคอมพิวเตอร์ของฉันคือ x64 แต่ฉันใช้เส้นทางที่ไม่ใช่ x64 gacutil.exe
และทำงานได้ ... สิ่งที่เฉพาะเจาะจงสำหรับ x64 อยู่ที่:
SDK: \ Program Files (x86) \ Microsoft SDKs \ Windows \ v10.0A \ bin \ NETFX 4.6.1 Tools \ x64 \ gacutil.exe
64 บิต IEต้องการ BHO ที่คอมไพล์แล้ว 64 บิตและ 64 บิตที่ลงทะเบียนไว้ แม้ว่าฉันจะสามารถดีบักโดยใช้ 32 บิต IE11 เท่านั้นส่วนขยายที่ลงทะเบียน 32 บิตนั้นก็ทำงานได้ด้วยการรัน 64 บิต IE11
คำตอบนี้ดูเหมือนจะมีข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้: https://stackoverflow.com/a/23004613/195417
ถ้าคุณต้องการคุณสามารถใช้ 64 บิต regasm:
% windir% \ Microsoft.NET \ Framework 64 \ v4.0.30319 \ RegAsm.exe
วิธีการทำงานของ Add-on นี้
ฉันไม่ได้เปลี่ยนพฤติกรรมของแอดออน ... ลองดูที่ IE8 ส่วนต่อไปเพื่อดูคำอธิบาย
## คำตอบก่อนหน้าสำหรับ IE8
ชาย ... นี่มันทำงานเยอะมาก! ฉันอยากรู้เกี่ยวกับวิธีการทำเช่นนี้ว่าฉันทำมันเอง
ก่อนอื่น ... เครดิตไม่ได้เป็นของฉันทั้งหมด นี่คือการรวบรวมสิ่งที่ฉันพบในเว็บไซต์เหล่านี้:
และแน่นอนฉันต้องการคำตอบของฉันที่จะมีคุณสมบัติที่คุณถาม:
- สำรวจเส้นทาง DOM เพื่อค้นหาบางสิ่งบางอย่าง
- ปุ่มที่แสดงหน้าต่าง (ในกรณีของฉันเพื่อตั้งค่า)
- ยังคงมีการกำหนดค่า (ฉันจะใช้รีจิสทรีเพื่อที่)
- และในที่สุดก็รันจาวาสคริปต์
ฉันจะอธิบายทีละขั้นตอนวิธีที่ฉันจัดการเพื่อทำงานกับ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ของปุ่มไปตกลง รหัสเหมือนกันสำหรับ add-on IE11
ในคุณสมบัติโครงการให้ทำดังต่อไปนี้:
- เซ็นชื่อแอสเซมบลีด้วยคีย์ - ที่แข็งแกร่ง
- ในแท็บดีบั๊กตั้งค่าเริ่มโปรแกรมภายนอกเป็น
C:\Program Files (x86)\Internet Explorer\iexplore.exe
- ในแท็บ Debug ตั้งอาร์กิวเมนต์บรรทัดคำสั่งเป็น
http://msdn.microsoft.com/en-us/library/ms976373.aspx#bho_getintouch
ในแท็บ Build Events ให้ตั้งค่าบรรทัดคำสั่ง Post-build eventsเป็น:
"C: \ Program ไฟล์ (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" / ถอนการลงทะเบียน "$ (TargetDir) $ (TargetFileName)"
"C: \ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ RegAsm.exe" "$ (TargetDir) $ (TargetFileName)"
ข้อควรระวัง:เนื่องจากคอมพิวเตอร์ของฉันคือ x64 มี x64 เฉพาะอยู่ในเส้นทางของ gacutil ที่สามารถเรียกใช้งานได้บนเครื่องของฉันซึ่งอาจแตกต่างจากของคุณ
64 บิต IEต้องการ BHO ที่คอมไพล์แล้ว 64 บิตและ 64 บิตที่ลงทะเบียนไว้ ใช้ RegAsm.exe 64 บิต (โดยปกติแล้วจะอยู่ใน C: \ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319 \ RegAsm.exe)
วิธีการทำงานของ Add-on นี้
มันสำรวจต้นไม้ DOM ทั้งหมดแทนที่ข้อความกำหนดค่าโดยใช้ปุ่มด้วยตัวเองด้วยพื้นหลังสีเหลือง หากคุณคลิกที่ข้อความสีเหลืองมันจะเรียกใช้ฟังก์ชันจาวาสคริปต์ที่แทรกอยู่บนหน้าเว็บแบบไดนามิก คำเริ่มต้นคือ 'เบราว์เซอร์' เพื่อให้ตรงกับพวกเขามาก!
แก้ไข:หลังจากเปลี่ยนสตริงที่จะเน้นคุณต้องคลิกกล่อง URL และกด Enter ... F5 จะไม่ทำงานฉันคิดว่ามันเป็นเพราะ F5 ถือเป็น 'นำทาง' และมันจะต้องฟังเหตุการณ์นำทาง (อาจจะ). ฉันจะพยายามแก้ไขในภายหลัง
ตอนนี้เป็นเวลาที่จะไป ฉันเหนื่อยมาก. อย่าลังเลที่จะถามคำถาม ... อาจเป็นเพราะฉันไม่สามารถตอบได้ตั้งแต่ฉันไปเที่ยว ... ใน 3 วันฉันกลับมาแล้ว แต่ฉันจะพยายามมาที่นี่ในเวลาเดียวกัน