如何开始开发Internet Explorer扩展?

有没有人在这里有开发IE扩展的经验,可以分享他们的知识? 这将包括代码示例,或链接到好的,或有关进程的文档,或任何东西。

我真的想这样做,但是我正在用一个糟糕的文档,糟糕的代码/示例代码/缺乏的巨大墙壁。 任何帮助/资源,你可以提供将不胜感激。

具体来说,我想开始如何访问/操纵在IE扩展内的DOM。

编辑,甚至更多的细节:

理想情况下,我想种植一个工具栏按钮,点击后,弹出一个菜单,其中包含指向外部网站的链接。 我也想访问DOM并根据一些条件在页面上植入JavaScript。

在IE扩展中保存信息的最佳方法是什么? 在Firefox / Chrome /大多数现代浏览器中,您使用window.localStorage ,但显然与IE8 / IE7,这不是一个选项。 也许一个SQLite数据库或这样的? 假设.NET 4.0将安装在用户的计算机上是可以的。

我不想使用Spice IE,因为我想创建一个与IE9兼容的IE。 我也为这个问题添加了C ++标记,因为如果在C ++中构建一个更好,我可以这样做。

    人…这已经很多工作了! 我很好奇如何做到这一点,我自己做到了。

    首先…信贷不是全部。 这是我在这些网站上发现的一个汇编:

    • CodeProject的文章 ,如何做一个BHO;
    • 15秒,但不是15秒,耗时约7小时;
    • 微软教程 ,帮助我添加命令按钮。
    • 而这个social.msdn话题 ,帮助我弄清楚这个程序集必须在GAC中。
    • 这个最近的MSDN博客文章包含了一个完整的示例
    • 许多其他网站,在发现过程中…

    当然,我想我的答案有你问的功能:

    • DOM遍历来找东西;
    • 一个显示窗口的按钮(在我的情况下设置)
    • 坚持配置(我将使用regitry)
    • 并最终执行JavaScript。

    我将逐步描述它,我如何设法使用Internet Explorer 8 ,在Windows 7 x64中 …请注意,我无法在其他配置中测试。 希望你明白=)

    创建一个工作的Internet Explorer 8插件

    我正在使用Visual Studio 2010C#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" /> 

    创建以下文件:

    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 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<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和一个OK Button 。 将按钮的DialogResult设置为Ok 。 将此代码放在表单代码中:

     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
    • 在“构建事件”选项卡中,将后构建事件命令行设置为:

        “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”/ unregister“$(TargetDir)$(TargetFileName)”
      
       “C:\ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ RegAsm.exe”“$(TargetDir)$(TargetFileName)” 

    注意:由于我的电脑是x64,我的机器上gacutil可执行文件的路径中有一个特定的x64,可能与您的不同。

    64位IE需要64位编译和64位注册的BHO。 使用64位RegAsm.exe(通常居于C:\ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319 \ RegAsm.exe)

    这个插件如何工作

    它遍历所有DOM树,替换使用按钮配置的文本,本身带有黄色背景。 如果你点击黄色的文本,它会调用一个插入页面的JavaScript函数。 默认的单词是“浏览器”,所以它匹配很多! 编辑:更改字符串后要突出显示,您必须单击URL框,然后按Enter … F5将无法正常工作,我认为这是因为F5被认为是“导航”,这将需要监听导航事件(也许)。 我会尽力解决这个问题。

    现在是时候了。 我很累。 随意提问…可能是因为我正在旅行,我不能回答…在3天内我回来了,但是我会尽力在这里过来。

    另一个很酷的方法是检查:

    http://www.crossrider.org

    这是一个基于JS和jQuery的框架,它可以让你使用一个共同的JS代码为IE,FF和Chrome开发浏览器扩展。 基本上这个框架完成了所有的恶意工作,而且你只需要编写你的应用程序代码。

    IE扩展的状态实际上是相当悲伤的。 你有IE5浏览器帮助对象的老模型(是的,那些臭名昭着的BHO,每个人都喜欢在白天阻止),工具栏和IE的新加速器。 即便如此,兼容性有时也会中断。 我曾经为IE6打破了IE7的扩展,所以有些事情已经改变了。 就大部分而言,据我所知(我多年来还没有碰到BHO),你仍然需要使用活动模板库(有些像微软的COM的STL)来编写代码,而且这样做只适用于C ++。 你可以用C#做​​COM互操作,然后用C#做这件事,但是它的价值可能太难了。 无论如何,如果您有兴趣编写自己的IE扩展(如果您希望在所有主流浏览器中都可以使用扩展程序,这是合理的),这里是官方的Microsoft资源。

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

    而对于IE8中的新加速器,你可以检查这个。

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

    我同意这个文档很糟糕,而且这些API太过时了。 不过,我希望这有助于。

    编辑:我想我可以在这里扔最后一个信息来源。 当我在BHO工作时,我正在浏览我的笔记。 这是让我从他们开始的文章。 这是有点旧,但有一个很好的ATL接口,你将使用IE浏览器BHO(例如IObjectWithSite)时使用的解释。 我觉得这个很好解释,当时帮了我很多。 http://msdn.microsoft.com/en-us/library/bb250436.aspx我也检查了GregC发布的例子&#x3002; 它至少可以和IE8一起工作,并且与VS 2010兼容,所以如果你想做C#,你可以开始在那里,看看Jon Skeet的书。 (C#深入第2版)第13章有很多有关C#4中新特性的信息,您可以使用它们与COM进行更好的交互。 (我仍然会推荐你用C ++来做你的插件)

    开发C#BHO是一个痛苦的屁股。 它涉及到很多恶心的COM代码和p / invoke调用。

    我有一个大部分完成的C#BHO 在这里 ,你可以自由地使用任何你想要的来源 。 我说“主要” ,因为我从来没有弄清楚如何在IE保护模式下保存appdata 。

    我一直在使用IE浏览器的浏览器控件已经有好几年了,在这个过程中,有一个名字一遍又一遍地出现在有用的贴子里:Igor Tandetnik

    如果我正在开发一个扩展,我会针对一个BHO,并开始搜索:

    BHO Igor Tandetnik

    要么

    浏览器助手对象Igor Tandetnik

    他的帖子通常非常详细,他知道他在说什么。

    在COM和ATL编程中你会发现自己的耳朵。 有关示例演练,请查看: http : //msdn.microsoft.com/en-us/library/ms976373.aspx

    我同意罗伯特·哈维,C#4.0功能改进COM互操作。 这里有一些旧的C#代码,迫切需要重写。

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

    这是试图通过避免ATL和使用Spartan COM来简化事情:

    C ++和COM来获取BHO

    如果你不想重新发明轮子,你可以试试IE的Add In Express 。 我已经使用了VSTO的东西 ,它的相当不错。 他们也有一个有用的论坛和快速的支持。

    这显然是解决了,但对于其他用户,我会推荐SpicIE框架 。 我已经根据它做出了自己的扩展。 它只支持Internet Explorer 7/8官方,但我测试了Internet Explorer 6-10 (从Windows XP到Windows 8 Consumer Preview),它工作正常 。 不幸的是,在最新版本中有一些错误,所以我不得不解决它们,并做出我自己的版本: http : //archive.msdn.microsoft.com/SpicIE/Thread/View.aspx?ThreadId=5251

    我热烈地建议你在2002年出版的Pavel Zolnikov的这篇文章!

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

    它基于Band对象的使用,并使用.Net 2.0进行编译。 源代码提供,并打开和Visual Studio 2013良好编译。正如你将阅读的帖子评论,它完美的IE 11和Windows 7和Windows 10的工作。它完全适合我在Windows 7 + SP1和IE 11享受!

    在这里输入图像描述

    在“生成事件”选项卡中,将生成后事件命令行设置为:(x64)如下所示

     "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)" 

    我想要生成事件选项卡,将后生成事件命令行设置为(32位操作系统)

     "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)"