在单独进程中运行 AxWebBrowser 控件
浏览器控件在混合应用程序开发中扮演着重要角色,当浏览器控件使用时间较长时,其内存占用会逐渐增加。本文介绍了一种将浏览器控件托管在单独的进程中,可以根据需要加载/卸载的方法。
引言
该示例利用 Microsoft AddIn Framework (MAF) 将浏览器控件托管在单独的进程中。这有助于确保导航到的网站的内存泄漏、错误和异常被隔离并在单独的进程中运行。这可以避免 Windows WPF 父进程因浏览的网站的任何问题而崩溃。
此应用程序以 URL 作为输入,并在单独的进程中导航到指定的 URL。宿主应用程序是一个 WPF Windows 应用程序。AddIn 浏览器控件是 axWebBrowser 的 WPF 包装器,它运行在名为 AddInProcess32 的单独进程中。它支持通过方法调用从宿主应用程序到 AddIn 的双向通信,以及通过事件机制从 AddIn 到宿主应用程序的通信。
Microsoft AddIn Framework 基础知识
对于 MAF 新手用户,请访问以下 MSDN 文档,以了解管道开发工作方式的基本架构。
浏览器宿主应用程序
加载浏览器:在单独的 AddIn 进程中加载浏览器控件,并在指定的框架内进行绘制。
卸载浏览器:卸载浏览器控件并终止 AddIn 进程。
导航:将浏览器导航到指定的 URL。
URL 列表框:显示从 BeforeNavigate 事件导航到的 URL。
从列表导航:选择一个 URL,然后单击此按钮导航到选定的 URL。
使用代码
在管道架构的每个层中都有七个项目引用。
项目 1:AppContracts
定义接口 IBrowserAddInContract
,该接口派生自 IContract
,并用 AddInContract
进行属性标记。IBrowserAddInContract
是在 AddIn 中实现的接口,用于在进程外使用 AddIn 框架运行浏览器。此接口有两个方法:一个用于获取句柄,另一个用于导航到指定 URL。
在导航之前,通知宿主它将导航到的 URL。这是基于事件实现的。使用 BeforeNavigateEventAdd 和 BeforeNavigateEventRemove 来添加/删除事件处理程序。
[AddInContract]
public interface IBrowserAddInContract : IContract
{
INativeHandleContract GetUI();
void Navigate(string url, string cookie);
void BeforeNavigateEventAdd(INavigateEventHandler handler);
void BeforeNavigateEventRemove(INavigateEventHandler handler);
}
定义两个接口:一个用于处理 AddIn 在宿主应用程序中引发的事件,另一个用于传递事件参数。
public interface INavigateEventHandler : IContract
{
bool Handler(INavigateEventArgs args);
}
public interface INavigateEventArgs : IContract
{
string url
{
get;
}
}
项目 2:HostSideAdapter
在此项目中,引用 AppContracts
和 BrowserHostView
项目。实现两个类:一个用于调用 ContractToHost 的方法,另一个用于调用 HostToContract 的方法。
ContractToView
:从 BrowserHostView
.BrowserAddIn 派生类 IsolatedElementContractToViewHostAdapter。用 HostAdapter
对类进行属性标记。声明一个事件和事件处理程序变量。通过组合引用 AppContract。
Contracts 实例由框架分配。该类执行 4 个功能:
- 实现 GetUI。
- 实现 Navigate。
- 触发 _Navigate 以调用事件。
- 添加和删除事件函数。
ViewToContract
:从 AppContracts
接口派生类。实现定义的所有方法。
项目 3:AddInSideAdapter
在此项目中,引用 AppContracts
和 BrowserAddInView
项目。
在此项目中需要处理的一个重要事项是用于运行事件的分派器。这是在 AppDispatcher
类中完成的。
ViewToContract
:此类依赖 AppDispatcher
来调用事件。从 AppContracts
接口派生类。用 AddInAdapterAttribute
对类进行属性标记。实现定义的所有方法。
ContractToView
:通过组合引用 AppContract。
项目 4 和 5:BrowserAddInView 和 BrowserHostView
两个项目都将视图类定义为抽象的。AppContracts
中的所有方法都已定义。仅将 AddInView 属性化为 [AddInBase]
。
public abstract class BrowserAddIn { public abstract event System.EventHandler<NavigateEventArgs> Navigating; public abstract FrameworkElement GetUI(); public abstract void Navigate(string url, string cookie); }
项目 6:BrowserHostApplication
在此项目中,引用 BrowserHostView
。如 LoadAddIn
方法中所述,找到 AddIn 的特定类型。使用 AddInStore
静态方法 FindAddIns
在特定路径下检索机器上可用的 AddIns。创建 AddIn 作为单独的进程,如下所示:
private void LoadAddIn()
{
String path = Environment.CurrentDirectory;
String[] warnings = AddInStore.Rebuild(path);
IList<AddInToken> tokens = AddInStore.FindAddIns(typeof(BrowserHostView.BrowserAddIn), path);
addins = new List<BrowserHostView.BrowserAddIn>();
ap = new AddInProcess();
ap.Start();
AddInStore.FindAddIns(typeof(BrowserHostView.BrowserAddIn), PipelineStoreLocation.ApplicationBase);
addInInstance = tokens[0].Activate<BrowserHostView.BrowserAddIn>(ap,
AddInSecurityLevel.FullTrust);
addins.Add(addInInstance);
addInIndex = dp.Children.Add(c.GetUI());
addInInstance.Navigating += new EventHandler<BrowserHostView.NavigateEventArgs>(usercontrol_BeforeNavigate);
}
要卸载 AddIn,请删除事件并关闭进程,如下所示:
private void UnloadAddIn()
{
addInInstance.Navigating -= usercontrol_BeforeNavigate;
dp.Children.RemoveAt(addInIndex);
addInInstance = null;
ap.Shutdown();
}
项目 7:BrowserAddIn
声明一个新类 BrowserAddInCode
并将其属性化为 [System.AddIn.AddIn("BrowserAddIn")]
。实现抽象视图类及其方法 GetUI
、Navigate
和 NavigatingEventCall
。此类实例化 BrowserControl
。
参考文献
所有这些都是通过引用以下文章实现的。这使我能够在我的项目中完成我正在寻找的任务。感谢文章中提供的贡献和示例。
Jack Gudenkauf 和 Jesse Kaplan 的 .NET Application Extensibility, Part 2
Chango V. - MSFT 的 Hosting WPF UI cross-thread and cross-process
Jesse Kaplan 的 AppDomain Isolated WPF Add-Ins
关注点
AddIn 架构提供了一种非常快速的方法,可以使用很少的代码行将任何控件托管在单独的进程中,而无需担心同步和进程间通信。