wpf
wpf copied to clipboard
RichTextBox insert text application will crash
- .NET Core Version: netcoreapp 3.1
- Windows version: Windows 10 CMGE
- Does the bug reproduce also in WPF for .NET Framework 4.8?: Yes
- Is this bug related specifically to tooling in Visual Studio (e.g. XAML Designer, Code editing, etc...)? No.
- Security issues and bugs should be reported privately, learn more via our responsible disclosure guidelines.
Problem description: the problem occur when we set the caret on the BlockUIContainer and typing any character,that will raise FatalExecutionEngineError in framework 4.7.2 or raise ExecutionEngineException in netcore 3.1.
How to reproduce?
- create an wpf project,and add this xaml code to mainwindow.xaml
<RichTextBox>
<FlowDocument>
<BlockUIContainer>
<Image Source="D:\sample.jpg"/>
</BlockUIContainer>
</FlowDocument>
</RichTextBox>
- use an image file to instead D:\sample.jpg
- run app
4.click image and press Arrow-Right
5.type any character
we test in another device,this problem reproduce again
@Luoyingliang This doesn't repro in latest .NET releases. Could you please confirm and check once?
reproduce repo is in here: https://github.com/Luoyingliang/RichTextBoxCrash
I cannot reproduce, neither in .NET FW 4.8, 4.7.2 nor in .NET 6.0.
ither in .NET FW 4.8, 4.7.2 nor in .NET 6.0.
emmm,maybe I can register an youtube account and upload a video ? In china this issue reproduce by many people
I think you can upload videos to GitHub. Try, maybe we will see something we missed in the repro steps.
the video in here https://user-images.githubusercontent.com/29297262/211973242-63f26cbf-4926-43b9-b001-0511b09d5f4e.mp4
Ohhh okay so the missing bit in the repro is that you are using an IME. "Typing any character" should be "start composition" - it does not repro with simple input methods such as US keyboard. I can reproduce now in both FW and Core. This is the failing assert:
https://github.com/dotnet/wpf/blob/c92f5ed2e8b0ae68cf67757c4f2634599a32d1d3/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/TextStore.cs#L3349-L3357
Ohhh okay so the missing bit in the repro is that you are using an IME. "Typing any character" should be "start composition" - it does not repro with simple input methods such as US keyboard. I can reproduce now in both FW and Core. This is the failing assert:
https://github.com/dotnet/wpf/blob/c92f5ed2e8b0ae68cf67757c4f2634599a32d1d3/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/TextStore.cs#L3349-L3357
thanks a lots
Ohhh okay so the missing bit in the repro is that you are using an IME. "Typing any character" should be "start composition" - it does not repro with simple input methods such as US keyboard. I can reproduce now in both FW and Core. This is the failing assert:
https://github.com/dotnet/wpf/blob/c92f5ed2e8b0ae68cf67757c4f2634599a32d1d3/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/TextStore.cs#L3349-L3357
Wait a minute, this issues also crash without ime composition
Ohhh okay so the missing bit in the repro is that you are using an IME. "Typing any character" should be "start composition" - it does not repro with simple input methods such as US keyboard. I can reproduce now in both FW and Core. This is the failing assert: https://github.com/dotnet/wpf/blob/c92f5ed2e8b0ae68cf67757c4f2634599a32d1d3/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/TextStore.cs#L3349-L3357
Wait a minute, this issues also crash without ime composition
right, using ime will crash
TextContainer/TextStore have inconsistent char counts!
so the problem is IMECharCount counting miss?
I think it has to do with the text pointer being inside the BlockUIContainer. Typing then has to create a paragraph, which adds a character to the IMECharCount but _netCharCount is not updated. Checking how it works without IME.
I think it has to do with the text pointer being inside the BlockUIContainer. Typing then has to create a paragraph, which adds a character to the IMECharCount but _netCharCount is not updated. Checking how it works without IME.
got it! thanks a lot
Looks like I was correct.
In the non-IME case, all changes are initiated from WPF, implicit paragraph is created and text put into it. Both changes cause TextStore.OnTextContainerChange to update and they update the _netCharCount.
In the IME case, changes are coming from TSF, and the composition comes in via TextStore.SetText, after the TSF issues a lock on the store. According to SetText documentation, ITextStoreACPSink::OnTextChange should not be called during the call and that is indeed the case, if there is a lock, WPF does not inform the sink of changes.
As a result, the implicit paragraph creation does not cause TextStore.OnTextContainerChange and therefore _netCharCount is not updated. We could fire the event, update the internal count & not inform the sink, however, then the inserted text will be counted twice - once in the OnTextContainerChange and once in the SetText method. The problem is that the SetText must return which range was affected, but without the events it doesn't know whether an extra paragraph had to be inserted or not.
I also suspect there is an undiscovered bug when filtering in TextBox takes place during IME composition, since the reported changed range uses the original text, not the filtered text.
One option would be to remember somewhere that a paragraph has been added and bump _netCharCount accordingly. Another option is to make the assumption that any changes in length during TextEditor.SetText are due to the text being set and follow the insertion point. Then the SetText could remember the length before and after change and add anything extra to the range changed.
the SetText could remember the length before and after change and add anything extra to the range changed.
This seems to fix the crash, but the composition gets confused.
Basically I think it is illegal to add a paragraph during SetText since TSF has a lock on the store. Citing the documentation:
Applications must never modify the document or send change notifications using the ITextStoreACPSink::OnTextChange method from within the ITextStoreACP::RequestLock method.
For the first option, if the _netCharCount is updated with the correct value in SetText, the crash is fixed (by definition) and the IME works uninterrupted, but I believe we should technically inform the sink that a paragraph has been added once the lock is lifted. This is a bit tricky because we need to track where the paragraph insertion ends up being after the composition.
It seems that the ExecutionEngineException or FatalExecutionEngineError can be also observed in case of Emoji panel, which is available in Windows 11:
- Using Visual Studio 2022, create a new project: C#, “WPF Application”, .NET 9.0,
- Open the MainWindow.xaml and insert the RichTextBox:
<RichTextBox>
<FlowDocument>
<Paragraph>
<Run>a</Run><Italic>b</Italic><Bold>c</Bold>
</Paragraph>
<Paragraph>
<Run>d</Run>
</Paragraph>
</FlowDocument>
</RichTextBox>
- Start the program in Debug mode,
- Select the whole text in RichTextBox using
<Ctrl + A>, - Press
<Win + ;>to display the Emoji panel, then click an emoji like “💕”, - The exception will be raised.
The problem does not seem to occur in Windows 10, or if the RichTextBox contains fewer or different elements, or if the emoji is pasted from Clipboard.
I hope that the problem can be reproduced on other computers.
@Viorel Can I know your stacktrace? Do you enable the Win11 text cursor indicator?
The Text Cursor Indicator was disabled. The error occurs regardless of this indicator. In both cases, the Stack Trace is:
[Managed to Native Transition]
> System.Private.CoreLib.dll!System.Environment.FailFast(System.Runtime.CompilerServices.StackCrawlMarkHandle mark, string message, System.Runtime.CompilerServices.ObjectHandleOnStack exception, string errorMessage) Line 369 C#
System.Private.CoreLib.dll!System.Environment.FailFast(ref System.Threading.StackCrawlMark mark, string message, System.Exception exception, string errorMessage) Line 83 C#
System.Private.CoreLib.dll!System.Environment.FailFast(string message) Line 44 C#
WindowsBase.dll!MS.Internal.Invariant.FailFast(string message, string detailMessage) Unknown
PresentationFramework.dll!System.Windows.Documents.TextStore.GrantLock() Unknown
PresentationFramework.dll!System.Windows.Documents.TextStore.GrantLockWorker(MS.Win32.UnsafeNativeMethods.LockFlags flags) Unknown
PresentationFramework.dll!System.Windows.Documents.TextStore.RequestLock(MS.Win32.UnsafeNativeMethods.LockFlags flags, out int hrSession) Unknown
[Native to Managed Transition]
[Managed to Native Transition]
PresentationFramework.dll!System.Windows.Documents.TextStore.OnLayoutUpdated() Unknown
PresentationFramework.dll!System.Windows.Documents.TextEditor.OnTextViewUpdatedWorker(object o) Unknown
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) Unknown
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) Unknown
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl() Unknown
WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(object obj) Unknown
System.Private.CoreLib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Line 179 C#
WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Unknown
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke() Unknown
WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue() Unknown
WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled) Unknown
WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled) Unknown
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) Unknown
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) Unknown
WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs) Unknown
WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(nint hwnd, int msg, nint wParam, nint lParam) Unknown
[Native to Managed Transition]
[Managed to Native Transition]
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame) Unknown
PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore) Unknown
PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) Unknown
WpfApp27.dll!WpfApp27.App.Main() Unknown
It seems that the exception is raised in TextStore.GrantLock().