Ошибка с настройкой RTF в Winforms при использовании функции поддержки бета-версии UTF-8 для всей Windows - PullRequest
10 голосов
/ 30 мая 2019

Я думаю, что нашел ошибку в Windows или .NET и ищу обходной путь.

Чтобы воспроизвести проблему, сначала включите функцию Windows «Бета: используйте Unicode UTF-8 для поддержки языков во всем мире».

enter image description here

Вам может потребоваться перезагрузить компьютер.

Теперь просто создайте два компонента RichTextBox в Winforms / C #, а затем добавьте событие:

    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: Каким-то образом отредактируйте строку RTF, чтобы она соответствовала неизвестным требованиям, которые должен иметь новый RTF, прежде чем настраивать RTF другого RichTextBox. Это кажется нелогичным, учитывая, что первый 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)

Ответы [ 2 ]

7 голосов
/ 03 июня 2019

Microsoft открыла исходные тексты библиотек WinForms, так что вы можете сами покопаться в исходном коде:

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

Метод StreamIn находится в строке 3140 https://github.com/dotnet/winforms/blob/master/src/System.Windows.Forms/src/System/Windows/Forms/RichTextBox.cs:

 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;
        }
    }

Это похоже на ошибку, и, поскольку это бета-версия, лучшим способом было бы зарегистрировать ее в Microsoft на https://developercommunity.visualstudio.com

Если вы замените свой класс управления 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

Об этом уже сообщалось в Microsooft: https://developercommunity.visualstudio.com/content/problem/544623/issue-caused-by-unicode-utf-8-for-world-wide-langu.html

Кайл Ванг из MSFT уже сузил вопрос об операционной системе:

PC1 (сборка ОС .437 может воспроизвести проблему):

Конверт:

enter image description here

Тест:
enter image description here

PC2 (OS Build. 348 не может воспроизвести проблему):

Env:

enter image description here

Тест:

enter image description here

3 голосов
/ 24 июня 2019

Из 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 framework до 4.7 или отключить бета-функцию. Эта проблема возникает в сборках Windows 1803 и 1809. Подобная тема ниже

RichTextBox.RTF установщик, выбрасывающий System.ArgumentException. Неверный формат файла в Windows версии 1803

...