dotnet 6 已知问题 获取 CultureInfo.NumberFormat 可能抛出 IndexOutOfRangeException 异常
本文记录一个 dotnet 6 已知问题,准确来说这是一个在 dotnet 5 引入的问题,到 dotnet 6.0.12 还没修。在获取 CultureInfo.NumberFormat 属性时,在一些奇怪的 Windows 设备上可能抛出 IndexOutOfRangeException 异常。本文将来告诉大家问题的原因和解决方法
最简复现代码
_ = new CultureInfo("en-US").NumberFormat;
在一些语言文化奇怪的系统上运行,以上代码将会抛出 IndexOutOfRangeException 异常。这个问题其中的一个影响就是会让 WPF 应用程序的 TextBlock 在布局时抛出 IndexOutOfRangeException 而失败。如果刚好全局捕获异常,且无视,那将会无限抛出
异常堆栈如下
> PresentationCore.dll!MS.Internal.TextFormatting.DigitState.HasLatinDigits(System.Globalization.CultureInfo culture)
PresentationCore.dll!MS.Internal.TextFormatting.DigitState.GetDigitCulture(System.Globalization.CultureInfo numberCulture, System.Windows.Media.NumberSubstitutionMethod method, out bool contextual)
PresentationCore.dll!MS.Internal.TextFormatting.DigitState.SetTextRunProperties(System.Windows.Media.TextFormatting.TextRunProperties properties)
PresentationCore.dll!MS.Internal.TextFormatting.SimpleRun.Create(MS.Internal.TextFormatting.FormatSettings settings, System.Windows.Media.TextFormatting.CharacterBufferRange charString, System.Windows.Media.TextFormatting.TextRun textRun, int cp, int cpFirst, int runLength, int widthLeft, int idealRunOffsetUnRounded, double pixelsPerDip)
PresentationCore.dll!MS.Internal.TextFormatting.SimpleTextLine.Create(MS.Internal.TextFormatting.FormatSettings settings, int cpFirst, int paragraphWidth, double pixelsPerDip)
PresentationCore.dll!MS.Internal.TextFormatting.TextFormatterImp.FormatLineInternal(System.Windows.Media.TextFormatting.TextSource textSource, int firstCharIndex, int lineLength, double paragraphWidth, System.Windows.Media.TextFormatting.TextParagraphProperties paragraphProperties, System.Windows.Media.TextFormatting.TextLineBreak previousLineBreak, System.Windows.Media.TextFormatting.TextRunCache textRunCache)
PresentationCore.dll!MS.Internal.TextFormatting.TextFormatterImp.FormatLine(System.Windows.Media.TextFormatting.TextSource textSource, int firstCharIndex, double paragraphWidth, System.Windows.Media.TextFormatting.TextParagraphProperties paragraphProperties, System.Windows.Media.TextFormatting.TextLineBreak previousLineBreak, System.Windows.Media.TextFormatting.TextRunCache textRunCache)
PresentationFramework.dll!System.Windows.Controls.TextBlock.MeasureOverride(System.Windows.Size constraint)
PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize)
PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize)
PresentationFramework.dll!System.Windows.Controls.Grid.MeasureOverride(System.Windows.Size constraint)
PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize)
PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize)
PresentationFramework.dll!MS.Internal.Helper.MeasureElementWithSingleChild(System.Windows.UIElement element, System.Windows.Size constraint)
PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize)
PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize)
PresentationFramework.dll!System.Windows.Controls.Decorator.MeasureOverride(System.Windows.Size constraint)
PresentationFramework.dll!System.Windows.Documents.AdornerDecorator.MeasureOverride(System.Windows.Size constraint)
PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize)
PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize)
PresentationFramework.dll!System.Windows.Controls.Border.MeasureOverride(System.Windows.Size constraint)
PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize)
PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize)
PresentationFramework.dll!System.Windows.Window.MeasureOverrideHelper(System.Windows.Size constraint)
PresentationFramework.dll!System.Windows.Window.MeasureOverride(System.Windows.Size availableSize)
PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize)
PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize)
PresentationCore.dll!System.Windows.Interop.HwndSource.SetLayoutSize()
PresentationCore.dll!System.Windows.Interop.HwndSource.RootVisualInternal.set(System.Windows.Media.Visual value)
PresentationFramework.dll!System.Windows.Window.SetRootVisualAndUpdateSTC()
PresentationFramework.dll!System.Windows.Window.SetupInitialState(double requestedTop, double requestedLeft, double requestedWidth, double requestedHeight)
PresentationFramework.dll!System.Windows.Window.CreateSourceWindow(bool duringShow)
PresentationFramework.dll!System.Windows.Window.ShowHelper(object booleanBox)
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler)
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl()
WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(object obj)
System.Private.CoreLib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)
WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext executionContext, System.Threading.ContextCallback callback, object state)
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke()
WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue()
WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled)
WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled)
WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o)
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler)
WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs)
WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(nint hwnd, int msg, nint wParam, nint lParam)
解决方法
可选以下任意方式解决
- 尝试修复用户错误的设置,因为这个设置不仅影响 .NET 系应用,同时也影响任何做好国际化多语言的应用。前往注册表的
HKEY_CURRENT_USER\Control Panel\International看一下sNativeDigits的内容,大部分情况下应该是0123456789才对。 另外,可以去控制面板的区域设置,更改日期、时间或数字格式,进入其他设置里面,点击重置 - 切换回 NLS 方案,切换方法是设置环境变量
DOTNET_SYSTEM_GLOBALIZATION_USENLS为 true 即可,详细请看 Globalization and ICU - .NET Microsoft Learn - 由于此问题在 .NET 7 运行时已修复,且官方无计划合入到 .NET 6 里。因此可以尝试更新自己的运行时到 .NET 7 版本,或者是挑拣 https://github.com/dotnet/runtime/pull/58598 的更改到自己的 .NET 6 运行时,进行私有发布
下图是注册表的配置:

如果是 .NET 系的非 WPF 应用,这个异常是在自己应用程序代码抛出的,可选采用忽略用户配置的方式,在创建 CultureInfo 对象时,可以传入参数,表示是否使用用户配置,如以下代码
var numberFormat = new CultureInfo("en-US", false).NumberFormat;
问题原因
此问题已报告给 .NET 官方,请看 https://github.com/dotnet/runtime/issues/83764
同步也报告给 WPF 官方,请看 https://github.com/dotnet/wpf/issues/7658
这个异常是在 .NET 5 引入的,根据官方文档可以了解到,在 .NET 5 之前,语言文化是调用平台相关的,也就是在 Windows 下调用的是 National Language Support (NLS) 进行语言文化格式化。在 .NET 5 才使用 International Components for Unicode (ICU) 进行格式化
而此问题就是设备的 ICU 存在问题导致的,更底层系统是什么问题,我没有了解到
在获取数字的语言文化时,将会进入 CultureData.GetNFIValues 方法,这个方法是采用如下代码来获取的
// LOCALE_SNATIVEDIGITS (array of 10 single character strings).
string digits = GetLocaleInfoCoreUserOverride(LocaleStringData.Digits);
nfi._nativeDigits = new string[10];
for (int i = 0; i < nfi._nativeDigits.Length; i++)
{
nfi._nativeDigits[i] = char.ToString(digits[i]);
}
以上代码存在一个问题,那就是 GetLocaleInfoCoreUserOverride 函数可能返回一个空字符串,这就导致了 nfi._nativeDigits[i] = char.ToString(digits[i]); 获取 10 个字符时抛出异常
什么时候 GetLocaleInfoCoreUserOverride 函数返回空字符串?这个函数底层将调用到 CultureData.IcuGetLocaleInfo 函数,代码如下
private string GetLocaleInfoCoreUserOverride(LocaleStringData type)
{
return ShouldUseUserOverrideNlsData ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type);
}
以上的 ShouldUseUserOverrideNlsData 默认值就是 false 值,只有设置环境变量 DOTNET_SYSTEM_GLOBALIZATION_USENLS 为 true 等方法才会是 true 的值,详细请看 Globalization and ICU - .NET Microsoft Learn
在 IcuGetLocaleInfo 函数里面是如此实现的,调用 GetLocaleInfoString 获取,如果获取失败,那就返回空字符串
bool result = Interop.Globalization.GetLocaleInfoString(localeName, (uint)type, buffer, ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY, uiCultureName);
if (!result)
{
// Failed, just use empty string
Debug.Fail("[CultureData.IcuGetLocaleInfo(LocaleStringData)] Failed");
return string.Empty;
}
return new string(buffer);
因此 GetLocaleInfoCoreUserOverride 是可能在此情况下返回空字符串的。由于这是 ICU 引入的问题,通过以上代码也可以知道,只需要让 ShouldUseUserOverrideNlsData 为 true 即可不调用 IcuGetLocaleInfo 方法,换成 NlsGetLocaleInfo 方法,走 NLS 从而修复此问题

在 .NET 7 将判断返回值是空字符串,准确来说是对于 10 个字符,将返回的格式化字符串数组而不是强行读取
此问题会影响到 WPF 的原因是在 WPF 里面,默认的 XAML 语言格式化,在没有明确设置时,将会使用 en-US 文化,这是在 FrameworkElement 里面定义的,请看代码
static public readonly DependencyProperty LanguageProperty =
DependencyProperty.RegisterAttached(
"Language",
typeof(XmlLanguage),
_typeofThis,
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage("en-US"),
FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsMeasure));
在 TextBlock 里面,将会使用此 XAML 语言文化获取 CultureInfo 类型,调用堆栈如下
> PresentationCore.dll!System.Windows.Markup.XmlLanguage.GetSpecificCulture()
PresentationFramework.dll!MS.Internal.Text.DynamicPropertyReader.GetCultureInfo(System.Windows.DependencyObject element)
PresentationFramework.dll!MS.Internal.Text.TextProperties.InitCommon(System.Windows.DependencyObject target)
PresentationFramework.dll!MS.Internal.Text.TextProperties.TextProperties(System.Windows.FrameworkElement target, bool isTypographyDefaultValue)
PresentationFramework.dll!System.Windows.Controls.TextBlock.GetLineProperties()
PresentationFramework.dll!System.Windows.Controls.TextBlock.EnsureTextBlockCache()
PresentationFramework.dll!System.Windows.Controls.TextBlock.MeasureOverride(System.Windows.Size constraint)
在 XmlLanguage.GetSpecificCulture 里面将会调用 CultureInfo.GetCultureInfoByIetfLanguageTag 获取 CultureInfo 类型。默认情况下传入参数就是 en-US 且会使用用户配置
通过异常的调用堆栈可以看到是在 DigitState.HasLatinDigits 函数里炸掉的,此函数的 culture 参数就是以上 XmlLanguage.GetSpecificCulture 获取到的值
> PresentationCore.dll!MS.Internal.TextFormatting.DigitState.HasLatinDigits(System.Globalization.CultureInfo culture)
PresentationCore.dll!MS.Internal.TextFormatting.DigitState.GetDigitCulture(System.Globalization.CultureInfo numberCulture, System.Windows.Media.NumberSubstitutionMethod method, out bool contextual)
PresentationCore.dll!MS.Internal.TextFormatting.DigitState.SetTextRunProperties(System.Windows.Media.TextFormatting.TextRunProperties properties)
PresentationCore.dll!MS.Internal.TextFormatting.SimpleRun.Create(MS.Internal.TextFormatting.FormatSettings settings, System.Windows.Media.TextFormatting.CharacterBufferRange charString, System.Windows.Media.TextFormatting.TextRun textRun, int cp, int cpFirst, int runLength, int widthLeft, int idealRunOffsetUnRounded, double pixelsPerDip)
PresentationCore.dll!MS.Internal.TextFormatting.SimpleTextLine.Create(MS.Internal.TextFormatting.FormatSettings settings, int cpFirst, int paragraphWidth, double pixelsPerDip)
PresentationCore.dll!MS.Internal.TextFormatting.TextFormatterImp.FormatLineInternal(System.Windows.Media.TextFormatting.TextSource textSource, int firstCharIndex, int lineLength, double paragraphWidth, System.Windows.Media.TextFormatting.TextParagraphProperties paragraphProperties, System.Windows.Media.TextFormatting.TextLineBreak previousLineBreak, System.Windows.Media.TextFormatting.TextRunCache textRunCache)
PresentationCore.dll!MS.Internal.TextFormatting.TextFormatterImp.FormatLine(System.Windows.Media.TextFormatting.TextSource textSource, int firstCharIndex, double paragraphWidth, System.Windows.Media.TextFormatting.TextParagraphProperties paragraphProperties, System.Windows.Media.TextFormatting.TextLineBreak previousLineBreak, System.Windows.Media.TextFormatting.TextRunCache textRunCache)
PresentationFramework.dll!System.Windows.Controls.TextBlock.MeasureOverride(System.Windows.Size constraint)
PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize)
PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize)
在 HasLatinDigits 里面用到了 CultureInfo 的 NumberFormat 属性,从而导致炸掉,请看代码
private static bool HasLatinDigits(CultureInfo culture)
{
string[] digits = culture.NumberFormat.NativeDigits;
for (int i = 0; i < 10; ++i)
{
string d = digits[i];
if (d.Length != 1 || d[0] != (char)('0' + i))
return false;
}
return true;
}
这就是为什么此 .NET 运行时的问题影响 WPF 应用的原因
即使修了 TextBlock 里的这个模块,依然还有其他好多模块可能炸掉,同时 .NET 运行时底层也在 .NET 7 修复了此问题,且用户端也有规避方法,因此我就关闭了问题
dotnet 6 已知问题 获取 CultureInfo.NumberFormat 可能抛出 IndexOutOfRangeException 异常的更多相关文章
- JavaWeb项目中获取对Oracle操作时抛出的异常错误码
最近在项目中碰到了这么一个需求,一个JavaWeb项目,数据库用的是Oracle.业务上有一个对一张表的操作功能,当时设置了两个字段联合的唯一约束.由于前断没有对重复字段的校验,需要在插入时如果碰到唯 ...
- C# Dictionary已知value获取对应的key
1:循环遍历法,分为遍历key-value键值对和遍历所有key两种形式 2:使用Linq查询法 private void GetDictKeyByValue() { Dictionary<in ...
- Java知多少(49)throw:异常的抛出
到目前为止,你只是获取了被Java运行时系统抛出的异常.然而,程序可以用throw语句抛出明确的异常.Throw语句的通常形式如下: throw ThrowableInstance;这里,Thr ...
- Java知多少(45)未被捕获的异常
在你学习在程序中处理异常之前,看一看如果你不处理它们会有什么情况发生是很有好处的.下面的小程序包括一个故意导致被零除错误的表达式. class Exc0 { public static void ma ...
- .net core获取数据库连接 抛出The type initializer to throw an exception
原文:https://www.cnblogs.com/pudefu/p/7580722.html 在.NET Framework框架时代我们的应用配置内容一般都是写在Web.config或者App.c ...
- Java基础进阶:时间类要点摘要,时间Date类实现格式化与解析源码实现详解,LocalDateTime时间类格式化与解析源码实现详解,Period,Duration获取时间间隔与源码实现,程序异常解析与处理方式
要点摘要 课堂笔记 日期相关 JDK7 日期类-Date 概述 表示一个时间点对象,这个时间点是以1970年1月1日为参考点; 作用 可以通过该类的对象,表示一个时间,并面向对象操作时间; 构造方法 ...
- 已知起始点,获取每段等距离途经点的经纬度(用百度js api作)
已知两个中文地址,自动规划路径,获取路径上每个3公里的点的经纬度 <html> <head> <meta http-equiv="Content-Type&qu ...
- java基础 File与递归练习 使用文件过滤器筛选将指定文件夹下的小于200K的小文件获取并打印按层次打印(包括所有子文件夹的文件) 多层文件夹情况统计文件和文件夹的数量 统计已知类型的数量 未知类型的数量
package com.swift.kuozhan; import java.io.File; import java.io.FileFilter; /*使用文件过滤器筛选将指定文件夹下的小于200K ...
- Acticiti流程引擎在已知当前流程定义id的情况下获取当前流程的所有信息(包括:节点和连线)
这里我们已知流程已经部署,我的需求是获取当前流程的所有任务节点,我使用instanceof关键字来进行匹配 private List<UserTask> getProcessUserTas ...
- 对象布局已知时 C++ 对象指针的转换时地址调整
在我调试和研究 netscape 系浏览器插件开发时,注意到了这个问题.即,在对象布局已知(即对象之间具有继承关系)时,不同类型对象的指针进行转换(不管是隐式的从下向上转换,还是强制的从上到下转换)时 ...
随机推荐
- QGIS中下载遥感影像的Python代码片段
1. 引言 QGIS是开源免费且好用的GIS软件,并且可以使用Python脚本来控制 这里主要使用的打开QGIS时启动内置的Python解释器,这个Python解释器可以访问iface接口从而实现对Q ...
- 记录--CSS 如何实现羽化效果?
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 最近碰到这样一个问题,在一张封面上直接显示书名,可能会存在书名看不太清楚的情况(容易受到背景干扰),如下 为了解决这个问题,设计师提了一个 ...
- 《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南 - 第7章
本章勘误: 暂无,等待细心的你告诉我哦. 本章注解: 暂无 本章释疑: 暂无,等待你的提问 致谢: MVP 林德熙 MVP 吕毅 sPhinX 相关链接 试读记录
- 【Java】请写出你最常见到的 5 个 runtime exception
请写出你最常见到的 5 个 runtime exception. 参考:https://blog.csdn.net/qq_20417499/article/details/80222820 Cla ...
- 应对 DevOps 中的技术债务:创新与稳定性的微妙平衡
技术性债务在DevOps到底意味着什么?从本质上讲,这是小的开发缺陷的积累,需要不断地返工.它可能由多种原因引起,例如快速交付新功能的压力,这可能会导致团队不得不牺牲代码的整洁和完善.但这些不完整的小 ...
- chrome开发者工具
官方文档 使用 DevTools 的工作区设置持久化 By Dave Gash Dave is a Tech Writer By Kayce Basques Technical Writer for ...
- #阶梯NIM#Poj 1704 Georgia and Bob
题目 有\(n\)个棋子摆放在x轴的正半轴上, 每次将棋子最多向左移动至上一个棋子之后(不能不移动), 不能操作为败,问先手是不是必胜 分析 这个模型可以转换成阶梯NIM的形式, 那么将奇数位置做NI ...
- 【Insights直播】3D建模服务,快速构建高质量3D模型
2021年7月15日,HMS Core 6.0面向全球开发者正式上线.华为在HMS Core 6.0中,为开发者开放了一个全新的服务--3D建模服务(3D Modeling Kit),为应用开发者提供 ...
- JS启动Windows上的exe
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Windows系统编译libhv带SSL,开启WITH_OPENSSL
需要开发一个https的服务,使用libhv来做,需要重新编译libhv,需要开启 WITH_OPENSSL,前面编译一直很顺利,但是打开VS生成动态库的时候,报错,找不到ssl相关的文件,看了官方的 ...