Ticket T888808
Visible to All Users

NullReferenceException is thrown in the GetFontCacheByFont method

created 5 years ago (modified 5 years ago)

[DevExpress Support Team: CLONED FROM T517263: The NullReference exception occurs when the LayoutChanged method is called]

Hi Support team,

I was going to raise a ticket for the same issue until i found this ticket already opened by Steven (feel free if you want me to raise a ticket instead of commenting this).

We are using the "BeginInvoke" and this issue still happens to us in our product, we are using a common method that takes a Control and a string as parameters, then sets the Control.Text = string, here's the method:

C#
internal void SetDisplayText(Control control, string text) { try { if (control == null) return; if (!control.IsHandleCreated) return; if (this.InvokeRequired || control.InvokeRequired) { BeginInvoke(new UIMarshallingThreadDelegates.ParameterLessTaskDelegate( delegate { control.Text = text; })); } else { control.Text = text; } } catch (Exception exception) { ServiceLocator.LoggerService.Error("TopOfBook.SetDisplayText => error : " + exception); } }

The method above can be called from several threads.

Here's the exception callStack:

Code
2020-05-04 14:27:45:832, P:29220, TH:62308, [Error,Error,4] : TopOfBook.SetDisplayText => error : System.NullReferenceException: Object reference not set to an instance of an object. at DevExpress.Utils.Text.FontsCache.GetFontCacheByFont(Graphics graphics, Font font) at DevExpress.Utils.Text.TextUtils.GetStringSize(Graphics g, String text, Font font, StringFormat stringFormat, Int32 maxWidth, Int32 maxHeight, IWordBreakProvider wordBreakProvider, Boolean& isCropped) at DevExpress.Utils.Paint.XPaintMixed.CalcTextSize(Graphics g, String s, Font font, StringFormat strFormat, Int32 maxWidth, Int32 maxHeight, Boolean& isCropped) at DevExpress.Utils.Paint.XPaintMixed.CalcTextSize(Graphics g, String s, Font font, StringFormat strFormat, Int32 maxWidth) at DevExpress.Utils.AppearanceObject.CalcTextSize(GraphicsCache cache, StringFormat sf, String s, Int32 width) at DevExpress.XtraEditors.ViewInfo.LabelControlViewInfo.CalcSimpleTextSize(String Text, Boolean useHotkeyPrefix, LabelAutoSizeMode mode, Int32 predWidth, Int32 predHeight) at DevExpress.XtraEditors.ViewInfo.LabelControlViewInfo.CalcTextSize(String Text, Boolean useHotkeyPrefix, LabelAutoSizeMode mode, Int32 predWidth, Int32 predHeight) at DevExpress.XtraEditors.ViewInfo.LabelControlViewInfo.CalcTextSize(String Text, Boolean useHotkeyPrefix, LabelAutoSizeMode mode) at DevExpress.XtraEditors.ViewInfo.LabelControlViewInfo.CalcTextSize(Boolean useHotkeyPrefix) at DevExpress.XtraEditors.ViewInfo.LabelControlViewInfo.CalcTextPoints() at DevExpress.XtraEditors.ViewInfo.LabelControlViewInfo.CalcContentRect(Rectangle bounds) at DevExpress.XtraEditors.ViewInfo.BaseControlViewInfo.CalcRects() at DevExpress.XtraEditors.ViewInfo.BaseControlViewInfo.CalcViewInfo(Graphics g) at DevExpress.XtraEditors.LabelControl.LayoutChanged(Boolean isVisualUpdate) at DevExpress.XtraEditors.LabelControl.OnTextChanged(EventArgs e) at System.Windows.Forms.Control.set_Text(String value) at Com.QuodFinancial.FrontEnd.Gui.Mvp.Views.ForEx.InstrumentSummary.InstrumentSummaryTopOfBook.SetDisplayText(Control control, String text)

The exception is very rare to happen, but in the context we are working at, this should never happen as we can miss big opportunities.
We are using DevExpress 18.2.5.

Thank you,
Chams

Show previous comments (1)
QF QF
Quod Financial 5 years ago

    Our project is very huge and is related to a complete platform and servers in order to run and display data, providing a simple project will be taking a lot of time from us.
    But what is most important that i can tell, is that the main method used is the one described above, and it is used in a multi-threaded context.

    Is it not enough to test the "InvokeRequired" before calling BeginInvoke (as you can see in our code above)? as it will be always false if the Handle is not created yet, and we'll not call the BeginInvoke. (that's something from the basic points that we have learned when we started using DevExpress years ago).

    Maybe if no clue is given we'll wait for the migration to 20.1.3.

    DevExpress Support Team 5 years ago

      Hi,

      Since you check both the this.InvokeRequired and control.InvokeRequired properties, would you please confirm that your controls indicated as control and this are created in the same UI thread?

      I investigated our source code and found that such an exception could be thrown only if the LabelControlViewInfo.CalcSimpleTextSize method was called in a background thread. We create and release a Graphics object in this method. If the method is accessed from a background thread, there is a chance that a Graphics object will not be created when the next PaintAppearance.CalcTextSize method is called.

      To check this point, please create a custom LabelControl and replace all your existing LabelControls with it using the Find and Replace VS dialog (CTRL+SHIFT+F):

      C#
      public class LabelControlEx: LabelControl { protected override BaseStyleControlViewInfo CreateViewInfo() { return new LabelControlViewInfoEx(this); } } public class LabelControlViewInfoEx : LabelControlViewInfo { public LabelControlViewInfoEx(BaseStyleControl obj) : base(obj) { } protected override Size CalcTextSize(string Text, bool useHotkeyPrefix, LabelAutoSizeMode mode, int predWidth, int predHeight) { Debug.WriteLine($"Id = {Thread.CurrentThread.ManagedThreadId}, IsBackground = {Thread.CurrentThread.IsBackground}"); return base.CalcTextSize(Text, useHotkeyPrefix, mode, predWidth, predHeight); } }

      Once the issue is reproduced, please copy the text from the Output window and send it to us.

        Hi DevExpress

        We also had this issue in our application and we were sure we did everything right with UI thread. And we did - we are using Invokes and Async/Await so there shouldn't be a thread issue.

        But what we didn't expect or didn't know for long time: On every .NET app, there is a global System.Threading.SynchronizationContext. When a .NET app starts, there will be a WindowsFormsSynchronizationContext registered on that property.
        In a 100% .NET app, you will most likely never have any issue. But if you have a part of your application in .NET and this part is hosted in another app type (for example C++), there can be the following problem:
        As soon as a .NET form is closed (after ShowDialog), the WindowsFormsSynchronizationContext is uninstalled on System.Threading.SynchronizationContext and a simple SynchronizationContext is registered. Note: SynchronizationContext != WindowsFormsSynchronizationContext.

        Source:
        https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Application.cs,3494
        https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/WindowsFormsSynchronizationContext.cs,163

        With SynchronizationContext registered on System.Threading.SynchronizationContext, thread issues can become real. In other words: With SynchronizationContext registered on System.Threading.SynchronizationContext, Invokes and Async/Await don't work correctly anymore.

        Solution
        In our main application at start (before any .NET form is shown), we do the following:

        Visual Basic
        Public Sub EnsureWindowsFormsSynchronizationContext() WindowsFormsSynchronizationContext.AutoInstall = False If SynchronizationContext.Current Is Nothing OrElse TypeOf SynchronizationContext.Current IsNot WindowsFormsSynchronizationContext Then SynchronizationContext.SetSynchronizationContext(New WindowsFormsSynchronizationContext()) End If End Sub

        Set AutoInstall to False, so the uninstall of WindowsFormsSynchronizationContext can't happen after a ShowDialog and (if needed) set your own WindowsFormsSynchronizationContext, which will remain as long as the app runs.

        Other sources:
        https://stackoverflow.com/questions/19535147/await-and-synchronizationcontext-in-a-managed-component-hosted-by-an-unmanaged-a
        https://stackoverflow.com/questions/32439669/hosting-net-and-winforms-synchronizationcontexts-is-reset-when-showdialog-of

        I hope this also helps in other tickets like: https://www.google.com/search?q=devexpress+exception+GetFontCacheByFont

        After this change, we didn't face the issue anymore.

        Best Regards,
        Silvan

        Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

        Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.