本文来告诉大家 WPF 已知问题,在用户的设备上,如果不存在 Arial 字体,同时安装了一些诡异的字体,那么也许就会让应用在使用到诡异的字体的时候,软件闪退

在 WPF 的 FontFamily.cs 字体类里面,有一个叫 FirstFontFamily 的属性,这个属性的逻辑代码里面将包括在当前字体太过诡异时,自动 Fallback 到默认的字体,而默认的字体就是 Arial 字体。这个属性将会在很多逻辑被调用,如获取 FamilyNames 时

        public LanguageSpecificStringDictionary FamilyNames
{
get
{
CompositeFontFamily compositeFont = FirstFontFamily as CompositeFontFamily;
if (compositeFont != null)
{
// Return the read/write dictionary of family names.
return compositeFont.FamilyNames;
}
else
{
// Return a wrapper for the cached family's read-only dictionary.
return new LanguageSpecificStringDictionary(FirstFontFamily.Names);
}
}
}

在进入到寻找 Fallback 字体将会进入到 Invariant 的 Assert 判断方法,在这里面找不到 Arial 字体时,将会进入 Environment.FailFast 让应用程序闪退

以下是 FirstFontFamily 属性的代码,代码我删除了不关键部分

 if (family == null)
{
FontStyle style = FontStyles.Normal;
FontWeight weight = FontWeights.Normal;
FontStretch stretch = FontStretches.Normal;
family = FindFirstFontFamilyAndFace(ref style, ref weight, ref stretch); if (family == null)
{
// 进入这里的逻辑将会去寻找 Fallback 字体
// fall back to null font
family = LookupFontFamily(NullFontFamilyCanonicalName);
Invariant.Assert(family != null);
}

在 LookupFontFamily 函数里面,将会尝试去寻找 Arial 字体,上面代码的 NullFontFamilyCanonicalName 默认就是使用 Arial 字体

        internal static readonly CanonicalFontFamilyReference NullFontFamilyCanonicalName = CanonicalFontFamilyReference.Create(null, "#ARIAL");

在 LookupFontFamily 函数里面将会调用 LookupFontFamilyAndFace 函数去寻找传入的字体,寻找的方法是从 _defaultFamilyCollection 去寻找传入的字体

这里的 _defaultFamilyCollection 是在静态构造时获取的,代码如下

        private static volatile FamilyCollection _defaultFamilyCollection = PreCreateDefaultFamilyCollection();

以上的 PreCreateDefaultFamilyCollection 函数,实际就是读取 WindowsFontsUriObject 列表,这里的 Windows 指的不是窗口,而是指 Windows 系统

        private static FamilyCollection PreCreateDefaultFamilyCollection()
{
FamilyCollection familyCollection = FamilyCollection.FromWindowsFonts(Util.WindowsFontsUriObject);
return familyCollection;
}

以上的 WindowsFontsUriObject 定义如下

        private const string WinDir = "windir";

            string s = Environment.GetEnvironmentVariable(WinDir) + @"\Fonts\";

            _windowsFontsLocalPath = s.ToUpperInvariant();

            _windowsFontsUriObject = new Uri(_windowsFontsLocalPath, UriKind.Absolute);

也就是说读取的就是 windir 文件夹下的 Fonts 文件夹,也就是 C:\Windows\Fonts\ 文件夹

在 LookupFontFamilyAndFace 将会尝试去从 C:\Windows\Fonts\ 文件夹寻找字体

                IFontFamily fontFamily = familyCollection.LookupFamily
(
canonicalFamilyReference.FamilyName,
ref style,
ref weight,
ref stretch
);

假定用户从 C:\Windows\Fonts\ 文件夹删除了 Arial 字体,那么将找不到字体,返回是空

也就是 LookupFontFamily 将返回空

        internal static IFontFamily LookupFontFamily(CanonicalFontFamilyReference canonicalName)
{
FontStyle style = FontStyles.Normal;
FontWeight weight = FontWeights.Normal;
FontStretch stretch = FontStretches.Normal; return LookupFontFamilyAndFace(canonicalName, ref style, ref weight, ref stretch);
}

在 FirstFontFamily 属性里面,判断字体存在的代码如下

 family = LookupFontFamily(NullFontFamilyCanonicalName);
Invariant.Assert(family != null);

在用户删除了 Arial 字体,将会让 family 是空,而在 Invariant 的定义代码如下

 internal static void Assert(bool condition)
{
if (!condition)
{
FailFast(null, null);
}
}

以上的 FailFast 方法将会调用 Environment.FailFast 方法

 private // DO NOT MAKE PUBLIC OR INTERNAL -- See security note
static void FailFast(string message, string detailMessage)
{
if (Invariant.IsDialogOverrideEnabled)
{
// This is the override for stress and other automation.
// Automated systems can't handle a popup-dialog, so let
// them jump straight into the debugger.
Debugger.Break();
} Debug.Assert(false, "Invariant failure: " + message, detailMessage); Environment.FailFast(SR.Get(SRID.InvariantFailure));
}

调用 Environment.FailFast 之后,应用程序就闪退了,只有在系统事件里面看到记录

我认为这是一个不合理的设计,至少在框架层不应该有这样的逻辑,作为一个十分成熟的 UI 框架,应该能兼容各个诡异的系统,我将这个问题报告给官方,请看 WPF known issues: Application will FailFast when not find the Arial font from system · Issue #4464 · dotnet/wpf

dotnet 读 WPF 源代码笔记 了解 WPF 已知问题 用户设备上不存在 Arial 字体将导致应用闪退的更多相关文章

  1. dotnet 读 WPF 源代码笔记 布局时 Arrange 如何影响元素渲染坐标

    大家是否好奇,在 WPF 里面,对 UIElement 重写 OnRender 方法进行渲染的内容,是如何受到上层容器控件的布局而进行坐标偏移.如有两个放入到 StackPanel 的自定义 UIEl ...

  2. dotnet 读 WPF 源代码笔记 渲染收集是如何触发

    在 WPF 里面,渲染可以从架构上划分为两层.上层是 WPF 框架的 OnRender 之类的函数,作用是收集应用程序渲染的命令.上层将收集到的应用程序绘制渲染的命令传给下层,下层是 WPF 的 GF ...

  3. 已知root用户密码并登录,修改mysql用户名密码方法

    首先进入命令行下:以root用户登录,命令:mysql -uroot -p 回车 输入密码:mysql>use mysql:mysql>UPDATE user SET password=P ...

  4. Android笔记--报错AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY in "create table if not exists user_info(导致APP闪退)

    问题描述 每次一运行,APP程序必定闪退,百度了发现,闪退问题绝大多数就跟sql语句有关: 看到控制台报出这样的错误: 百度发现,我忘记了最初的知识点: 在表里面,自动递增是在数据类型为Integer ...

  5. 《深入浅出WPF》笔记——资源篇

    原文:<深入浅出WPF>笔记--资源篇 前面的记录有的地方已经用到了资源,本文就来详细的记录一下WPF中的资源.我们平时的“资源”一词是指“资财之源”,是创造人类社会财富的源泉.在计算机程 ...

  6. WPF自学笔记

    WPF使用哪几种元素作为顶级元素: 1. Window元素 2. Page元素(与Window元素类似,用于可导航的应用程序) 3. Application元素(定义应用程序资源和启动设置) PS:在 ...

  7. 《深入浅出WPF》笔记——绘画与动画

    <深入浅出WPF>笔记——绘画与动画   本篇将记录一下如何在WPF中绘画和设计动画,这方面一直都不是VS的强项,然而它有一套利器Blend:这方面也不是我的优势,幸好我有博客园,能记录一 ...

  8. 《深入浅出WPF》笔记——模板篇

    原文:<深入浅出WPF>笔记--模板篇 我们通常说的模板是用来参照的,同样在WPF中,模板是用来作为制作控件的参照. 一.认识模板 1.1WPF菜鸟看模板 前面的记录有提过,控件主要是算法 ...

  9. 【HLSL学习笔记】WPF Shader Effect Library算法解读之[DirectionalBlur]

    原文:[HLSL学习笔记]WPF Shader Effect Library算法解读之[DirectionalBlur] 方位模糊是一个按照指定角度循环位移并叠加纹理,最后平均颜色值并输出的一种特效. ...

  10. 【HLSL学习笔记】WPF Shader Effect Library算法解读之[Embossed]

    原文:[HLSL学习笔记]WPF Shader Effect Library算法解读之[Embossed] Embossed(浮雕效果)          浮雕效果主要有两个参数:Amount和Wid ...

随机推荐

  1. 【Spring注解驱动开发】@PostConstruct与@PreDestroy源码的执行过程

    写在前面 在前面的<[String注解驱动开发]你真的了解@PostConstruct注解和@PreDestroy注解吗?>一文中,我们简单的介绍了@PostConstruct注解与@Pr ...

  2. layui select onchange事件处理

    1.html代码如下,不要忘记 lay-filter属性 <div class="layui-input-block">         <select id=& ...

  3. cyc_to_led

    Entity: cyc_to_led File: cyc_to_led.v Diagram Generics Generic name Type Value Description MD_SIM_AB ...

  4. verilog设计知识集合(2)

    verilog设计知识集合(2) 1.阻塞与非阻塞 阻塞赋值是存在先后关系的,非阻塞是不存在先后关系的.一般而言,阻塞用于组合逻辑,非阻塞用于时序逻辑(不一定).阻塞的执行时逐步赋值,非阻塞是同步赋值 ...

  5. KingabseES 隐式游标属性值(SQL%attribute)

    隐式游标介绍 Oracle数据库迁移到KingbaseES数据库,不需要将源PL/SQL脚本,大规模修改为KES语法,因为KingbaseES支持大部分PLSQL语法. 1.隐式游标 隐式游标是由 P ...

  6. 如何安装和使用Docker

    本文深入解析Docker,一种革命性的容器化技术,从其基本概念.架构和组件,到安装.配置和基本命令操作.文章探讨了Docker在虚拟化.一致性环境搭建及微服务架构中的关键作用,以及其在云计算领域的深远 ...

  7. 2024-03-30:用go语言,集团里有 n 名员工,他们可以完成各种各样的工作创造利润, 第 i 种工作会产生 profit[i] 的利润,它要求 group[i] 名成员共同参与, 如果成员参与

    2024-03-30:用go语言,集团里有 n 名员工,他们可以完成各种各样的工作创造利润, 第 i 种工作会产生 profit[i] 的利润,它要求 group[i] 名成员共同参与, 如果成员参与 ...

  8. CI和自动化测试的结合(jenkins的搭建和使用)

    CI持续集成是一种思想,具体实现是通过jenkins持续集成工具去实现的. Jenkins的安装和配置(war包启动) 安装jenkins的方式有两种: 方式一:通过下载安装包安装,jenkins.m ...

  9. PicGo图床配置github仓库上传typora图片

    (前提是已注册github并新建一个仓库作为你上传图片的位置) 首先在PicGo官网下载软件:https://picgo.github.io/PicGo-Doc/zh/ 打开typora,找到偏好设置 ...

  10. python爬虫等获取实时数据+Flume+Kafka+Spark Streaming+mysql+Echarts实现数据动态实时采集、分析、展示

    使用爬虫等获取实时数据+Flume+Kafka+Spark Streaming+mysql+Echarts实现数据动态实时采集.分析.展示 [获取完整源码关注公众号:靠谱杨阅读人生 回复kafka获取 ...