dotnet 读 WPF 源代码笔记 了解 WPF 已知问题 用户设备上不存在 Arial 字体将导致应用闪退
本文来告诉大家 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 字体将导致应用闪退的更多相关文章
- dotnet 读 WPF 源代码笔记 布局时 Arrange 如何影响元素渲染坐标
大家是否好奇,在 WPF 里面,对 UIElement 重写 OnRender 方法进行渲染的内容,是如何受到上层容器控件的布局而进行坐标偏移.如有两个放入到 StackPanel 的自定义 UIEl ...
- dotnet 读 WPF 源代码笔记 渲染收集是如何触发
在 WPF 里面,渲染可以从架构上划分为两层.上层是 WPF 框架的 OnRender 之类的函数,作用是收集应用程序渲染的命令.上层将收集到的应用程序绘制渲染的命令传给下层,下层是 WPF 的 GF ...
- 已知root用户密码并登录,修改mysql用户名密码方法
首先进入命令行下:以root用户登录,命令:mysql -uroot -p 回车 输入密码:mysql>use mysql:mysql>UPDATE user SET password=P ...
- Android笔记--报错AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY in "create table if not exists user_info(导致APP闪退)
问题描述 每次一运行,APP程序必定闪退,百度了发现,闪退问题绝大多数就跟sql语句有关: 看到控制台报出这样的错误: 百度发现,我忘记了最初的知识点: 在表里面,自动递增是在数据类型为Integer ...
- 《深入浅出WPF》笔记——资源篇
原文:<深入浅出WPF>笔记--资源篇 前面的记录有的地方已经用到了资源,本文就来详细的记录一下WPF中的资源.我们平时的“资源”一词是指“资财之源”,是创造人类社会财富的源泉.在计算机程 ...
- WPF自学笔记
WPF使用哪几种元素作为顶级元素: 1. Window元素 2. Page元素(与Window元素类似,用于可导航的应用程序) 3. Application元素(定义应用程序资源和启动设置) PS:在 ...
- 《深入浅出WPF》笔记——绘画与动画
<深入浅出WPF>笔记——绘画与动画 本篇将记录一下如何在WPF中绘画和设计动画,这方面一直都不是VS的强项,然而它有一套利器Blend:这方面也不是我的优势,幸好我有博客园,能记录一 ...
- 《深入浅出WPF》笔记——模板篇
原文:<深入浅出WPF>笔记--模板篇 我们通常说的模板是用来参照的,同样在WPF中,模板是用来作为制作控件的参照. 一.认识模板 1.1WPF菜鸟看模板 前面的记录有提过,控件主要是算法 ...
- 【HLSL学习笔记】WPF Shader Effect Library算法解读之[DirectionalBlur]
原文:[HLSL学习笔记]WPF Shader Effect Library算法解读之[DirectionalBlur] 方位模糊是一个按照指定角度循环位移并叠加纹理,最后平均颜色值并输出的一种特效. ...
- 【HLSL学习笔记】WPF Shader Effect Library算法解读之[Embossed]
原文:[HLSL学习笔记]WPF Shader Effect Library算法解读之[Embossed] Embossed(浮雕效果) 浮雕效果主要有两个参数:Amount和Wid ...
随机推荐
- 【Spring注解驱动开发】@PostConstruct与@PreDestroy源码的执行过程
写在前面 在前面的<[String注解驱动开发]你真的了解@PostConstruct注解和@PreDestroy注解吗?>一文中,我们简单的介绍了@PostConstruct注解与@Pr ...
- layui select onchange事件处理
1.html代码如下,不要忘记 lay-filter属性 <div class="layui-input-block"> <select id=& ...
- cyc_to_led
Entity: cyc_to_led File: cyc_to_led.v Diagram Generics Generic name Type Value Description MD_SIM_AB ...
- verilog设计知识集合(2)
verilog设计知识集合(2) 1.阻塞与非阻塞 阻塞赋值是存在先后关系的,非阻塞是不存在先后关系的.一般而言,阻塞用于组合逻辑,非阻塞用于时序逻辑(不一定).阻塞的执行时逐步赋值,非阻塞是同步赋值 ...
- KingabseES 隐式游标属性值(SQL%attribute)
隐式游标介绍 Oracle数据库迁移到KingbaseES数据库,不需要将源PL/SQL脚本,大规模修改为KES语法,因为KingbaseES支持大部分PLSQL语法. 1.隐式游标 隐式游标是由 P ...
- 如何安装和使用Docker
本文深入解析Docker,一种革命性的容器化技术,从其基本概念.架构和组件,到安装.配置和基本命令操作.文章探讨了Docker在虚拟化.一致性环境搭建及微服务架构中的关键作用,以及其在云计算领域的深远 ...
- 2024-03-30:用go语言,集团里有 n 名员工,他们可以完成各种各样的工作创造利润, 第 i 种工作会产生 profit[i] 的利润,它要求 group[i] 名成员共同参与, 如果成员参与
2024-03-30:用go语言,集团里有 n 名员工,他们可以完成各种各样的工作创造利润, 第 i 种工作会产生 profit[i] 的利润,它要求 group[i] 名成员共同参与, 如果成员参与 ...
- CI和自动化测试的结合(jenkins的搭建和使用)
CI持续集成是一种思想,具体实现是通过jenkins持续集成工具去实现的. Jenkins的安装和配置(war包启动) 安装jenkins的方式有两种: 方式一:通过下载安装包安装,jenkins.m ...
- PicGo图床配置github仓库上传typora图片
(前提是已注册github并新建一个仓库作为你上传图片的位置) 首先在PicGo官网下载软件:https://picgo.github.io/PicGo-Doc/zh/ 打开typora,找到偏好设置 ...
- python爬虫等获取实时数据+Flume+Kafka+Spark Streaming+mysql+Echarts实现数据动态实时采集、分析、展示
使用爬虫等获取实时数据+Flume+Kafka+Spark Streaming+mysql+Echarts实现数据动态实时采集.分析.展示 [获取完整源码关注公众号:靠谱杨阅读人生 回复kafka获取 ...