本文来告诉大家 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. Markdown的习题

    markdown的使用说明 习题1: 将这段话改为2级标题 习题2 试着在你的'Typora'中编辑下面的内容: 这是第一行 这是第2行 这是补充内容 这是第3行 习题3 将下面的内容改为指定的格式要 ...

  2. WinAppSDK / WinUI3 项目无法使用 SystemEvents 的问题

    SystemEvents 是一个开发 win32 窗口项目很常用的类,其中封装了一些常用的系统广播消息.在 WinUI3 项目中,SystemEvents 事件经常无法触发,简单排查了一下原因. Sy ...

  3. Android常用布局之LinearLayout线性布局和RealtiveLayout相对布局

    LinearLayout最常用的属性: id layout_width layout_height background 外边距:layout_margin:也是有好多方向 layout_margin ...

  4. mybatis踩坑之integer类型是0的时候会被认为0!=''是假

    当你的参数类型是integer类型,并且传的是0的时候,在SQL里面做if判断的时候 <if test="auditStatus != null and auditStatus != ...

  5. 轻松上手Jackjson(珍藏版)

    写在前面 虽然现在市面上有很多优秀的json解析库,但 Spring默认采用Jackson解析Json. 本文将通过一系列通俗易懂的代码示例,带你逐步掌握 Jackson 的基础用法.进阶技巧以及在实 ...

  6. win7系统部署django项目

        win7系统部署django项目,在Windows电脑上我使用的是  Apache + mod_wsgi 来部署的   # 背景 Python 3.8.3 Django 3.1.7 Apach ...

  7. Windows系统自定义盘符图标

    记录一个小知识: 自定义Windows系统盘符的图标,其实这个东西很简单,就像设置U盘的图标一样 首先准备一张ico图片,如果没有ico图片,只有jpg或其他格式的,可以使用这个在线转ico的网站,把 ...

  8. Djangorestframework 记录一个报错 -- rest_framework.authentication.ToKenAuthentication

    今天在使用 Djangorestframework 这个框架时,发生报错: ImportError: Could not import 'rest_framework.authentication.T ...

  9. nginx重新整理——————http请求的11个阶段中的preaccess[十四]

    前言 简单整理一下preaccess. 正文 主要是介绍一下上文提及到的limit_req以及limit_conn. 里面是http_limit_conn_module 默认编译进去. 生效范围: 全 ...

  10. spring cloud 学习笔记 服务注册与发现(二)

    前言 服务注册与发现的学习.这个其实是微服务的核心了,因为微服务的一个重要理念就是将项目拆分,达到解耦的地步.那么如何把这些服务联系到一起就很关键. 如果一个服务到另外一个服务通过ip地址之间访问,虽 ...