网络浏览器控制限制
Posted
技术标签:
【中文标题】网络浏览器控制限制【英文标题】:Webbrowser Control Limitations 【发布时间】:2010-12-02 12:42:02 【问题描述】:我在 Windows 窗体 C# 项目中使用 WebBrowser 控件,想知道您可以同时运行多少个此类应用程序实例是否有任何限制。 (换句话说,MSFT 是否强制执行除物理机器限制之外的任何限制 - CPU/内存等)
【问题讨论】:
我刚刚为您发布了一个关于 WebBrowser 控件限制的答案(长篇),并且还想纠正 WebBrowser 控件的渲染引擎使用当前安装的 IE 版本引擎的想法(因为它没有),我已经概述了如何预测和查看它用于渲染的版本,具体取决于您安装的内容。 (请参阅我的回答)但基本上,它默认使用 IE 4.0 或 IE 7.0 进行渲染,这可以通过注册表进行更改。干杯。 【参考方案1】:让我告诉你一些缺点......
[这里提到的大部分问题已经在我之前在 *** 的答案部分中得到了一定程度的解答或解决,如果你好奇,请随意浏览我的 WebBrowser-Control 相关答案]。
检测页面何时真正完成加载很难可靠地做到,事实上,你必须使用一系列黑客才能做到这一点,甚至没有谈论一些方法和想法在线并且不为人所知,但多年来我一直在与这种控制作斗争,我已经弄清楚了一些事情并开发了一个代码库来使它工作!确实如此,如果您需要这方面的帮助,我可以提供更多详细信息。
让我直截了当地告诉你。默认渲染引擎开启 webbrowser 控件是固定的,以确保在所有的兼容性 平台。
基本上,如果你安装的浏览器是 IE 7 - IE 9,那么 使用的渲染引擎仅 IE 7.0(默认)。
但是,如果您安装的 IE 版本是 IE 6 或更低,那么 使用的渲染引擎是 IE 4.0(不是开玩笑),当然除非你 否则设置它。
有一种误解认为 WebBrowser 控件使用任何 当前已安装(当前 IE 版本),但事实并非如此,因为 他们这样做是为了减少向后兼容性问题。你可以看到 (作为证据)这确实是你的问题 www.whatsmyuseragent.com 在您的普通浏览器中,然后转到 该网站再次在您的 WebBrowser 控件中,您将看到它 说 MSIE 7.0 :)。
您可以将其设置为使用当前安装的互联网版本 资源管理器,在页面内使用 META 标记,或编辑注册表 在将运行 webbrowser 控件的机器上(编辑 Current_User 和 Local_Machine 都可以工作)。
因此,出于兼容性原因,默认情况下它将以 IE7 标准模式呈现页面。为防止这种情况发生,请点击我在下面提供的链接,该链接将讨论 META 标记方法和注册表编辑方法来解决此问题(适用于 32 位和 64 位系统)。该解决方案包含在对其他人关于某个功能不正确或意外运行的问题的回答中。阅读问题对于正确解释/理解答案不是必需的。这是链接:
Script runs slower in the dotnet WebBrowser control(Ctrl + 单击以在新选项卡中打开)。
事件系统非常 hacky,您确实需要了解尚未正确记录的内容以及根本未记录的内容。事实上,我已经宣布它是微软最糟糕的产品之一,就产品的设计而言,以及他们在其上提供的缺乏体面的文档方面。他们枯燥的 MSDN 风格文档很可笑。
错误的框架支持,如果你调用 document.frames.length,你只会得到顶层文档下的框架,而不是所有的框架,你需要编写自己的函数来获取所有嵌套框架(无限嵌套),如果您需要帮助,我已经这样做了。框架检测和引用非常重要,在检测页面何时真正完成加载时起着至关重要的作用。在那方面,在 WebBrowser 控件上使用 .Busy 和 .ReadyState 是不够的。事实上,这还远远不够。
没有内置系统可以摆脱每个页面中弹出的 javascript 对话框,包括新的 IE9 对话框,它会用“你确定要离开这个页面吗”消息来纠缠人们.我已经开发了执行此操作并摆脱它们的例程,基本上,其中一种方法涉及执行从 WebBrowser 控件发送到 html 页面的 JavaScript,指示它摆脱警报、确认、打印对话框(以及获取摆脱了我之前提到的新的 IE 9 对话框)。这些是单独来自 JS 的潜在对话框,我基本上运行 JavaScript,告诉浏览器 .alert 函数为 Null(即:一个什么都不做的空方法/函数),我对所有这些都做完全相同的事情4 个来自 JavaScript 的对话框。当然,如果您数了超过 4 个盒子(如果您数了更多,请随时告诉我)。此外,还有第二种方法可以做到这一点,它不仅会阻止 JavaScritp 对话框,而且会阻止可能/将出现在 webbrowser 控件中的每个对话框,此方法使用 WinHooks,并拦截之前的对话框显示时,您可以从对话框中获取尽可能多的信息(其内容为文本,标题/标题为文本等)并决定是否要显示或取消其显示,甚至模拟单击任何部分对话框(即:其任何按钮),因此堆栈认为问题或信息对话框已得到正确响应。这是一个有趣的方法,我已经读过但还没有尝试过,我真的很期待一旦我有空闲时间了解 WinHook 过程。像往常一样,如果您需要帮助,请随时查看我之前对各种网络浏览器控制问题的回答,因为我已经回答了很多问题,如果没有,请告诉我。请记住,这在很大程度上取决于知道页面何时完全加载完成,这很难做到(但可以使用未记录的方法,以 100% 可靠的方式)。所以第1点)。将多次出现相关性。
没有可靠或简单的方法来控制永久或保存的缓存信息,再次,您必须开发自己的例程来对缓存信息执行您想要的操作,过滤、删除或尝试以防止所有缓存类型出现这种情况,包括历史信息、cookie 和存储在本地系统上的实际缓存文件。如果您查看 DeleteUrlCacheEntry ,它将为您提供两种自行完成的方法,我很确定我以前有一些关于如何在 *** 上执行此操作的答案。使用 DeleteUrlCacheEntry,您可以使用以“Cookie:”标签、“Visited:”标签开头的缓存项目以及只是普通网站地址的项目(以“http://”和“https://”开头(是的,https 被缓存了 ;|,至少位置信息是无论如何)。另请注意,可通过 DeleteUrlCacheEntry 获得的此信息(以及用于循环遍历整个缓存的随附 FindFirstUrlCacheEntry/FindNextUrlCacheEntry)不包括您的实际互联网浏览器历史项目。“已访问:”站点列表与您的实际历史列表是分开的,当您单击 Internet Explorer 菜单栏上的 * 符号并进入“历史”部分(来自收藏夹部分)时,您会看到该列表。我是不知道他们为什么这样做,以及确切的形式差异是什么(以及为什么存在差异),但它在要找出的事情清单上(请随时在 cmets 中告诉我们)。因为, “已访问:”列表是您拥有的网站的列表访问过,而 IE 历史记录几乎是您访问过的网站的列表。我认为它们不会区分您手动输入和输入的网站与 HTML 页面或浏览器自动检索的点点滴滴(例如通过 iframe 等,以及自动重定向、弹出窗口等) ...所以我发现很难理解区别是什么,一旦我发现我会更新这一点。
覆盖默认用户代理没有正确内置,您可以将自己的用户代理传递给导航方法,但是一旦用户导航到那里,该站点将获取您的程序用户代理详细信息但是,这不会永久存在。因此,一旦用户点击导航页面上的链接,WebBrowser 控件将继续发送 WB 控件用于呈现您的站点的实际(真实)用户代理,当然,除非您拦截导航,取消它并使用重新导航.navigate 方法再次发送您自己的用户代理(再次)。这将无法考虑图像和 LINK 标记文件等内容,因为您没有收到这些的 BeforeNavigate 事件,因此您无法拦截它们并修改为它们发送的标头。相反,您需要通过导入一些外部函数 urlmon.dll 来使用外部解决方案 - 这可以做到 100% 并且可以完美运行,但是,它是另一个附加的依赖项(但 urlmon.dll 包含在迄今为止的所有相关 Windows 版本中)。
没有“将我的所有 WB 控件活动重定向到此特定框架”属性或方法,但如果您愿意或需要,您可以并且必须开发,唯一的框架支持是 TargetFrameName 参数.navigate 方法附带的,您需要获取对它的引用并手动指导您在那里执行的所有操作,因为用户可以从任何框架单击内容而您没有除非你检查它的想法或线索。
框架指向外部域的站点的跨框架安全性:如您所知,如果您在 abc.com 上有一个页面,并且该页面具有来自名为 xzy.com 的域的源 (就像大多数广告商在从他们自己的服务器中继内容时所做的那样),如果您尝试访问该框架,您将遇到跨框架域安全问题,无论您的应用程序在何种提升权限下运行。这很愚蠢,他们甚至不会告诉您,相反,您指向框架的参考文档中将没有任何数据,您将无法使用它,并且 WB 控件不会告诉您原因。您将可以访问的只是框架的源 URL,仅此而已,其中没有任何内容。解决方案?好吧,您的机器上有一个可注册的 TypeLib,您可以使用它来覆盖它,而不是内置到 WB 控件中,甚至也没有内置到您自己的编程接口中,实际上它是一个外部 C 例程,您需要通过引用来使用并注册 TypeLib(不确定现在在.NET 中是否有没有这种方法的新方法)。但是,您还需要在当前编程环境中围绕此 TypeLib 编写代码(使用 TypeLib 注册中的内容的多余代码,因此这不仅仅是调用函数的问题,而是围绕该函数编写更多代码,您将使用)。
打开/关闭 JavaScript,打开/关闭导航设置,例如导航声音等。如果您正在编写网页提取程序,导航声音会让您的用户发疯,打开或关闭这些选项是没有内置到 WebBrowser 控件中,如果需要,您可以使用注册表全局更改内容,然后在完成后将其更改回来。您将需要查找这些与 Internet 设置相关的设置/选项中的每一个的 reg 值。有一些方法可以为您的应用程序实例执行此操作,我相信从 InternetSecuritySettings 导入例程,但再一次,不是内置到 WB 中,而是要添加到列表中的另一系列黑客。
当然,您需要检测互联网连接是否存在,以及是否可用。 WB Control 甚至不会让您对此抱有一丝希望,尽管它是让它发挥作用的重要部分。因此,如果您不希望 MS 拨号连接(对于那些使用拨号的用户)的烦人弹出窗口或其他连接上的互联网向导,每次您的 WB 控件尝试建立连接或尝试导航到某个地方时,然后您将需要使用控件来尝试手动检查连接,并且此控件必须是 MS 外部的控件,并且控件的核心没有 MS API(因为 MS Internet API 是 API触发这些用于互联网连接的弹出框)。因此,您需要从头开始编写一个不使用winsocks 的外部winsocks 类型控件,学习如何使用它,并在每次使用WB 执行操作之前使用它来尝试检查互联网是否已连接控制。
当您处理实时 html 文档/页面上的元素时,您会收到很多“自动化错误”或“未指定错误”消息,它们甚至不会告诉您出了什么问题,这些是通常当有 html 以不推荐的方式完成时,即使它是浏览器可以处理和读取的一种方式,并且会定期处理。例如,如果您有一个 target=_top 的 Anchor 链接,并且在 _top 部分周围没有引号,即使浏览器理解这一点并按预期运行,webbrowser 控件也会举手放弃,抛出一个“未指定的错误”——甚至没有告诉你它对什么超级挑剔。因此,您必须确保元素是这样编写的: target="_top" 以使 WB 控件正常运行,并且对每个实时文档进行这些更改可能很乏味,您需要如果需要,请为每个页面编写通用例程 - 在文档完全加载后运行的例程(您必须可靠地检测才能执行此操作)。如果我必须选择最难的事情来正确使用 WB 控件,它必须检测页面何时完全、可靠地加载。最重要的是,使用 WB 控制也是您需要做的最重要的事情,因为几乎所有事情都取决于对此的准确检测。
它需要一个单独的历史对象,因为如果您在导航过程中选择“无历史”,或者找到一种使无历史导航工作的方法,您可以确定返回或前进到这些页面不会工作(即:调用 .GoBack 或 .GoForward 到这些页面和地址)。一旦您从历史记录中删除,或指定不保留此导航或特定导航的历史记录,除非您重新导航到该页面,否则不可能返回那里。他们应该保留一个内存中的历史列表,即使该页面已从全局历史中删除(这是它进行无历史浏览的唯一方式),该列表也应该可以返回。因此,如果您尝试返回,您将(最重要的是)遇到运行时错误,并且仅在最近的 .NET 日子里,他们才提供了一个名为 .CanGoBack 的方法来检查您是否可以返回,在那之前(如果使用 pre.NET)你应该围绕这个编写代码或尝试记录你在哪里(这不容易做到,但仍然可行)。
我可以继续(我认为),但我暂时将其保留,但是,除了这些之外,它是一个非常酷的控件,并且确实为您打开了一个全新的应用程序和想法世界的大门可以实现。正如我在其中的几点中所指出的,这些都是我已经解决的问题(还有更多,当需要解决方案时我已经解决了),所以如果您有任何问题或需要任何帮助,让我知道,因为我很乐意至少尝试帮助你。
当我试图弄清楚这些东西时,周围没有人可以帮助我,因为没有人真正了解这个控制,所以我不得不一点一点,一个一个地弄清楚。从那时起,它开始流行起来,使用它的人也越来越多(尤其是在 .NET 版本提供了渐进式改进之后)。因此,我很乐意帮助遇到我以前遇到过的情况的任何人,因为我记得那是一个可怕而孤独的地方,而且 MS 没有在文档方面做任何事情。这只是他们为内部使用而开发并让其他人使用的东西,同时仅提供所有属性、方法和事件的输入/输出参数/参数和返回值列表,仅此而已 - 没有意义或上下文或与之相关的真实代码示例,当然,在解决随之而来的一系列问题方面,没有任何文档明智。
好的,暂时就这样吧,有兴趣了解人们对此控件和使用它的看法,所以请随时发表评论。小心。错误。
【讨论】:
感谢您的出色回答。我也无法检测页面何时完成加载。你能给我提供更多细节吗?我尝试了很多方法,但仍然无法正常工作 你好,我对你承诺提供帮助的修复感兴趣,请帮助我提供信息 大家好,我很乐意为您提供帮助,但是这些问题中的每一个都需要一个新问题,因此您需要针对每个问题提出一个新问题,然后指出我给他们,这样我就可以对它做出回应。 非常好的答案!如果可以的话 +100 @Jack 非常感谢您的赞赏,当我看到它为他人带来了好处时,它完全值得写:)。【参考方案2】:WebBrowser
控件没有人为限制。
但是,它使用 IE 的渲染引擎(无论最终用户计算机上安装了什么版本),因此它使用了相当多的内存。
你想做什么?
如果您正在尝试编写网络浏览器,我建议您使用更好的渲染引擎,例如WebKit 或Gecko。
【讨论】:
这样的选择涉及一系列权衡。如果您发布 WebKit 或 Gecko,安装会更大,并且每次发现这些组件的安全漏洞时,您都必须发布补丁。另一方面,安装 IE 更新时会更新 IE 组件。 是的。然而,使用 IE 引擎意味着他将增加 IE6 的用户群。 mashable.com/2009/07/16/ie6-must-die 我很了解其他渲染引擎,并且优点缺点 - 但是我需要使用 IE。问题仍然是限制是什么?我还预计会出现内存利用率问题,但是当我对 10 个实例以上进行一些测试时,我遇到了某种障碍……这不是内存,它似乎与 CPU 相关,但考虑到机器有 8 个内核,这没有意义. 我不知道为什么会这样。 @SLaks 很抱歉地通知您,这是不正确的,它不使用当前安装的版本。我会发布一个详细说明规则的答案。【参考方案3】:试试这段代码,看看会发生什么:
int count = 0;
List<Form> forms = new List<Form>();
try
while (true)
Form f = new Form();
WebBrowser wb = new WebBrowser();
f.Controls.Add(wb);
f.Show();
wb.Url = new Uri(@"http://www.***.com");
forms.Add(f);
count++;
catch
MessageBox.Show(count.ToString());
我猜是几百个,但我不知道。
【讨论】:
将 try/catch 移到while
循环之外,否则你将永远不会停止循环。
@SLaks:完成。你让我感到羞耻。 :) 我实际上在想,一旦 MessageBox 弹出,我只需点击 Visual Studio 中的stop
按钮。
@MusiGenesis 我讨厌在弹出消息框时我们不能按停止,它迫使我多次关闭 devenv 进程:(。以上是关于网络浏览器控制限制的主要内容,如果未能解决你的问题,请参考以下文章