当前位置: 首页 > news >正文

WPF 跨应用程序域的 UI(Cross AppDomain UI)

为自己写的程序添加插件真的是一个相当常见的功能,然而如果只是简单加载程序集然后去执行程序集中的代码,会让宿主应用程序暴露在非常危险的境地!因为只要插件能够运行任何一行代码,就能将宿主应用程序修改得天翻地覆哭爹喊娘;而根本原因,就在于暴露了整个托管堆和整个 UI 树。

如果将宿主和插件放到不同的应用程序域中,则可以解决此问题。本文将介绍跨应用程序域承载 UI 的方法,其中也包含跨域(Cross-Domain)调用方法。


来自于托管插件框架的辅助类

.NET Framework 自 3.5 以来推出了托管插件框架(MAF,Managed AddIn Framework),位于 System.AddIn 命名空间。其特性在于,将宿主和插件隔离在不同的应用程序域中,避免插件对宿主造成不良影响。

此命名空间中存在 FrameworkElementAdapters 类型,在 System.Windows.Presentation 程序集中,详见 FrameworkElementAdapters.cs。虽说主要用于 MAF 插件框架,但其实只需要此类型便可以实现跨应用程序域的 UI。

FrameworkElementAdapters 只有两个方法,ViewToContractAdapter 将 UI 转换成 INativeHandleContract,而 ContractToViewAdapterINativeHandleContract 用一个 FrameworkElement 进行承载。

public static class FrameworkElementAdapters
{
    [SecurityCritical]
    public static FrameworkElement ContractToViewAdapter(INativeHandleContract nativeHandleContract);
    [SecurityCritical]
    public static INativeHandleContract ViewToContractAdapter(FrameworkElement root);
}

一个极简的跨域 UI Demo

首先,我们需要有一个支持跨域调用的类型,并有任意的可以用来返回 INativeHandleContract 的方法。

internal sealed class DomainX : MarshalByRefObject
{
    public INativeHandleContract GetElement()
    {
        return FrameworkElementAdapters.ViewToContractAdapter(
            new Rectangle
            {
                Width = 200,
                Height = 100,
                Fill = Brushes.ForestGreen,
            });
    }
}

我们需要跨域创建这个 UI 控件并得到 INativeHandleContract

var domain = AppDomain.CreateDomain("X");
var instance = (DomainX)domain.CreateInstanceAndUnwrap(typeof(DomainX).Assembly.FullName, typeof(DomainX).FullName);
var contract = instance.GetElement();

然后,在需要承载这个跨域 UI 的地方取得这个 INativeHandleContract 的实例 contract

var element = FrameworkElementAsyncAdapters.ContractToViewAdapter(contract);
// this 在这里是 MainWindow 或者 MainPage,或者其它任何能够承载 FrameworkElement 的对象。
this.Content = element;

以上的这两端代码都可以写在 MainWindowLoaded 事件中。

对 MAF 吐一下槽

MAF 框架对插件和宿主程序集所在的文件夹结构有要求。这可是非常讨厌的一项特性!因为当我们希望采用 MAF 框架的时候,我们的应用程序可能已经有自己独特的一套目录了。就算我们从零开始写应用,采用 MAF 约定的方式组织 dll 也是很丑的方式(带有很重的 MAF 的影子)。

它没有提供任何的配置,而且如果不按照约定放置文件夹,还会发生如下错误:


参考资料

  • ENikS/System.AddIn: Projects related to Microsoft System.AddIn
  • Add-In Performance: What can you expect as you cross an isolation boundary and how to make it better [Jesse Kaplan] – CLR Add-In Team Blog
  • WPF Add-Ins Overview - Microsoft Docs
  • Walkthrough: Creating an Extensible Application - Microsoft Docs
  • Add-ins and Extensibility - Microsoft Docs

相关文章:

  • 将 UWP 的有效像素(Effective Pixels)引入 WPF
  • 用动画的方式画出任意的路径(直线、曲线、折现)
  • 使 WPF 支持触摸板的横向滚动
  • NullReferenceException,就不应该存在!
  • 当我们使用 MVVM 模式时,我们究竟在每一层里做些什么?
  • 分享一个算法,计算能在任何背景色上清晰显示的前景色
  • WPF 绘制对齐像素的清晰显示的线条
  • 让 ScrollViewer 的滚动带上动画
  • UI 设计中的视觉无障碍设计
  • 为什么委托的减法(- 或 -=)可能出现非预期的结果?(Delegate Subtraction Has Unpredictable Result)
  • 将美化进行到底,使用 Oh My Posh 把 PowerShell 做成 oh-my-zsh 的样子
  • 实现一个 WPF 版本的 ConnectedAnimation
  • C#/.NET 中的契约
  • WPF 自定义键盘焦点样式(FocusVisualStyle)
  • 异步任务中的重新进入(Reentrancy)
  • hexo+github搭建个人博客
  • Android优雅地处理按钮重复点击
  • C++类中的特殊成员函数
  • css属性的继承、初识值、计算值、当前值、应用值
  • fetch 从初识到应用
  • Git学习与使用心得(1)—— 初始化
  • Java 网络编程(2):UDP 的使用
  • JavaScript设计模式与开发实践系列之策略模式
  • JS实现简单的MVC模式开发小游戏
  • leetcode讲解--894. All Possible Full Binary Trees
  • Linux下的乱码问题
  • log4j2输出到kafka
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • SpringBoot几种定时任务的实现方式
  • underscore源码剖析之整体架构
  • 从零开始的无人驾驶 1
  • 基于组件的设计工作流与界面抽象
  • 实现简单的正则表达式引擎
  • 一文看透浏览器架构
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 最简单的无缝轮播
  • k8s使用glusterfs实现动态持久化存储
  • 第二十章:异步和文件I/O.(二十三)
  • ​520就是要宠粉,你的心头书我买单
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • ​二进制运算符:(与运算)、|(或运算)、~(取反运算)、^(异或运算)、位移运算符​
  • #etcd#安装时出错
  • #Java第九次作业--输入输出流和文件操作
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (未解决)jmeter报错之“请在微信客户端打开链接”
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • ..thread“main“ com.fasterxml.jackson.databind.JsonMappingException: Jackson version is too old 2.3.1
  • .NET 应用架构指导 V2 学习笔记(一) 软件架构的关键原则
  • .NET关于 跳过SSL中遇到的问题