使用 Windows 范围的 beta UTF-8 支持功能时在 Winforms 中调整 RTF 的错误

Posted

技术标签:

【中文标题】使用 Windows 范围的 beta UTF-8 支持功能时在 Winforms 中调整 RTF 的错误【英文标题】:Bug with adjusting RTF in Winforms when using Windows-wide beta UTF-8 support feature 【发布时间】:2019-10-16 09:00:48 【问题描述】:

我认为我在 Windows 或 .NET 中发现了一个错误,正在寻找解决方法。

要重现该问题,首先启用 Windows 功能“测试版:使用 Unicode UTF-8 获得全球语言支持”。

您可能需要重新启动机器。

现在只需在 Winforms/C# 中创建两个 RichTextBox 组件,然后添加事件:

    private void richTextBox1_TextChanged(object sender, EventArgs e)
    
        string s = richTextBox1.Rtf;
        richTextBox2.Rtf = s;
    

最后,运行程序并简单地在第一个 RichTextBox 中输入一些内容,当它尝试写入richTextBox2.Rtf 时会崩溃并显示“文件格式无效”消息。如果禁用 Windows 功能“Beta:使用 Unicode UTF-8 支持全球语言”,它不会崩溃。

我正在考虑两种潜在的解决方法:

1:在 C# 应用程序中以某种方式禁用了整个“测试版:使用 Unicode UTF-8 支持全球语言”功能,并假装它从一开始就没有启用。

2:在调整另一个 RichTextBox 的 RTF 之前,以某种方式编辑 RTF 字符串以符合新 RTF 应具有的任何未知要求。考虑到第一个 RichTextBox 无论如何都应该具有完全相同的 RTF,这似乎违反直觉,但无论如何......


************* Exception Text **************
System.ArgumentException: File format is not valid.
at System.Windows.Forms.RichTextBox.StreamIn(Stream data, Int32 flags)
at System.Windows.Forms.RichTextBox.StreamIn(String str, Int32 flags)
at System.Windows.Forms.RichTextBox.set_Rtf(String value)
at unicodeTesting.Form1.richTextBox1_TextChanged(Object sender, EventArgs e) in D:\Code\c#\_tests\unicodeTesting\Form1.cs:line 30
at System.Windows.Forms.Control.OnTextChanged(EventArgs e)
at System.Windows.Forms.TextBoxBase.OnTextChanged(EventArgs e)
at System.Windows.Forms.TextBoxBase.WmReflectCommand(Message& m)
at System.Windows.Forms.TextBoxBase.WndProc(Message& m)
at System.Windows.Forms.RichTextBox.WmReflectCommand(Message& m)
at System.Windows.Forms.RichTextBox.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

【问题讨论】:

什么崩溃?是从第一个文本框读取,还是写入第二个文本框?什么是完整的堆栈跟踪? @canton7: 写到秒。如果这就是“完整堆栈跟踪”的意思,则编辑问题以澄清并添加异常文本。 嗯,它是测试版。 RTF 很不稳定,是最后剩下的文本格式之一,它的核心是基于代码页的,不能在 Unicode 编码中完成。您可能会在本世纪完成并用于写字板小程序的 RichTextBox 版本中取得进展。 ***.com/a/34358642/17034 我看到了很多与 beta UTF-8 语言环境设置相关的错误,所以这并不奇怪 试过韩的建议了吗?我可以重现您的问题的唯一方法是针对 .Net 版本 【参考方案1】:

微软开源了 WinForms 库,所以你可以自己挖掘源代码:

https://github.com/dotnet/winforms/tree/master/src/System.Windows.Forms/src/System/Windows/Forms

StreamIn 方法在 https://github.com/dotnet/winforms/blob/master/src/System.Windows.Forms/src/System/Windows/Forms/RichTextBox.cs 的第 3140 行:

 private void StreamIn(string str, int flags)
    
        if (str.Length == 0)
        
            // Destroy the selection if callers was setting
            // selection text
            //
            if ((RichTextBoxConstants.SFF_SELECTION & flags) != 0)
            
                SendMessage(Interop.WindowMessages.WM_CLEAR, 0, 0);
                ProtectedError = false;
                return;
            
            // WM_SETTEXT is allowed even if we have protected text
            //
            SendMessage(Interop.WindowMessages.WM_SETTEXT, 0, "");
            return;
        

        // Rather than work only some of the time with null characters,
        // we're going to be consistent and never work with them.
        int nullTerminatedLength = str.IndexOf((char)0);
        if (nullTerminatedLength != -1)
        
            str = str.Substring(0, nullTerminatedLength);
        

        // get the string into a byte array
        byte[] encodedBytes;
        if ((flags & RichTextBoxConstants.SF_UNICODE) != 0)
        
            encodedBytes = Encoding.Unicode.GetBytes(str);
        
        else
        
            encodedBytes = Encoding.Default.GetBytes(str);
        
        editStream = new MemoryStream(encodedBytes.Length);
        editStream.Write(encodedBytes, 0, encodedBytes.Length);
        editStream.Position = 0;
        StreamIn(editStream, flags);
    

    private void StreamIn(Stream data, int flags)
    
        // clear out the selection only if we are replacing all the text
        //
        if ((flags & RichTextBoxConstants.SFF_SELECTION) == 0)
        
            NativeMethods.CHARRANGE cr = new NativeMethods.CHARRANGE();
            UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), Interop.EditMessages.EM_EXSETSEL, 0, cr);
        

        try
        
            editStream = data;
            Debug.Assert(data != null, "StreamIn passed a null stream");

            // If SF_RTF is requested then check for the RTF tag at the start
            // of the file.  We don't load if the tag is not there
            // 
            if ((flags & RichTextBoxConstants.SF_RTF) != 0)
            
                long streamStart = editStream.Position;
                byte[] bytes = new byte[SZ_RTF_TAG.Length];
                editStream.Read(bytes, (int)streamStart, SZ_RTF_TAG.Length);
                string str = Encoding.Default.GetString(bytes);
                if (!SZ_RTF_TAG.Equals(str))
                
                    throw new ArgumentException(SR.InvalidFileFormat);
                

                // put us back at the start of the file
                editStream.Position = streamStart;
            

            int cookieVal = 0;
            // set up structure to do stream operation
            NativeMethods.EDITSTREAM es = new NativeMethods.EDITSTREAM();
            if ((flags & RichTextBoxConstants.SF_UNICODE) != 0)
            
                cookieVal = INPUT | UNICODE;
            
            else
            
                cookieVal = INPUT | ANSI;
            
            if ((flags & RichTextBoxConstants.SF_RTF) != 0)
            
                cookieVal |= RTF;
            
            else
            
                cookieVal |= TEXTLF;
            
            es.dwCookie = (IntPtr)cookieVal;
            es.pfnCallback = new NativeMethods.EditStreamCallback(EditStreamProc);

            // gives us TextBox compatible behavior, programatic text change shouldn't
            // be limited...
            //
            SendMessage(Interop.EditMessages.EM_EXLIMITTEXT, 0, int.MaxValue);



            // go get the text for the control
            // Needed for 64-bit
            if (IntPtr.Size == 8)
            
                NativeMethods.EDITSTREAM64 es64 = ConvertToEDITSTREAM64(es);
                UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), Interop.EditMessages.EM_STREAMIN, flags, es64);

                //Assign back dwError value
                es.dwError = GetErrorValue64(es64);
            
            else
            
                UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), Interop.EditMessages.EM_STREAMIN, flags, es);
            

            UpdateMaxLength();

            // If we failed to load because of protected
            // text then return protect event was fired so no
            // exception is required for the the error
            if (GetProtectedError())
            
                return;
            

            if (es.dwError != 0)
            
                throw new InvalidOperationException(SR.LoadTextError);
            

            // set the modify tag on the control
            SendMessage(Interop.EditMessages.EM_SETMODIFY, -1, 0);

            // EM_GETLINECOUNT will cause the RichTextBoxConstants to recalculate its line indexes
            SendMessage(Interop.EditMessages.EM_GETLINECOUNT, 0, 0);


        
        finally
        
            // release any storage space held.
            editStream = null;
        
    

这看起来确实是一个错误,因为它是测试版,所以最好的做法是通过https://developercommunity.visualstudio.comMicrosoft 记录它

如果您将 RichTextBox 控件类替换为库中的代码,您将能够看到错误发生在哪一行:

System.Windows.Forms.RichTextBox.StreamIn(Stream data, Int32 flags)

更新:

这实际上是一个已知问题, https://social.msdn.microsoft.com/Forums/en-US/28940162-5f7b-4687-af19-1eeef90d3963/richtextboxrtf-setter-throwing-systemargumentexception-file-format-is-not-valid-in-windows?forum=winforms

已向微软报告:https://developercommunity.visualstudio.com/content/problem/544623/issue-caused-by-unicode-utf-8-for-world-wide-langu.html

来自 MSFT 的 Kyle Wang 已将其范围缩小为操作系统问题:

PC1(OS Build .437 可以重现该问题):

环境:

测试:

PC2(OS Build .348 无法重现该问题):

环境:

测试:

【讨论】:

嗯,我假设您指的是扩展 RichTextBox 类并覆盖 StreamIn 方法。问题是我无法解决RichTextBoxConstantsNativeMethodsGetErrorValue64HandleRef 等问题,而且还有很多编译错误。 检查我的更新,看起来它已经在微软的雷达上,他们已经缩小了范围。似乎重新引入了回归。【参考方案2】:

来自 MSDN,当尝试设置 RTF 时,它会检查起始字符串是否等于“\rtf”,但是当启用此功能时,格式将以“\urtf”开头,这会导致显式微软抛出异常。

MSDN 参考:

string str = Encoding.Default.GetString(bytes);

if (!SZ_RTF_TAG.Equals(str)) // SZ_RTF_TAG ="\\rtf";

    throw new ArgumentException(SR.GetString(SR.InvalidFileFormat));

为避免这种情况,您需要将 .net 框架升级到 4.7 或禁用 beta 功能。此问题将出现在 Windows 1803 和 1809 版本中。 类似的线程在下面

RichTextBox.RTF setter throwing System.ArgumentException. File format is not valid in Windows version 1803

【讨论】:

您好,感谢您对此问题的现有研究和故障排除。我的理解是.Net 4.7 如果操作系统版本是 1803 或 1809,它会导致 ArgumentException?您是说 .Net 4.7 可以在 1803 和 1809 上运行吗?顺便说一句,我已经为您解决了 Microsoft Connect 案例,他们关闭它有点狡猾。 有趣。目前,我将使用 Hans 的 RichEdit50 解决方案,因为我现在想继续使用 .NET 3.5,而 .NET 4.7 可能还是会使用 RichEdit50。 @DanW I'd like to stay with .NET 3.5 for now 该运行时在几年前就失去了支持。支持的最早版本是 .NET 4.5.2。如果您想使用不受支持的运行时,请不要启用 beta 功能。在该运行时发现的问题不会得到修复 是的,虽然我是前 15 名赏金猎人,但你活该! @PanagiotisKanavos:我不启用也不关心这个 UTF8 测试版功能;但是我的一些用户会这样做,当它崩溃时他们会感到困惑。那就是问题所在。 .NET 3.5 可用于更广泛的 PC,因此我有理由坚持使用它。

以上是关于使用 Windows 范围的 beta UTF-8 支持功能时在 Winforms 中调整 RTF 的错误的主要内容,如果未能解决你的问题,请参考以下文章

Beta版: 使用 unicode UTF-8 提供全球语言支持(U)

Windows 记事本的 ANSI,Unicode,UTF-8 这三种编码模式有啥区别

iOS 7.1 beta 5 uitableviewcell 高度显示其范围之外的对象

无法在 Windows 7 64 位上使用 stlsoft-1.9.124 编译 pantheios 1.0.0-beta216

NPM 3 Beta为Windows用户带来利好消息

微软发布 Windows 11 首个 Beta 版本