WPF FormattedText “The system cannot find the file specified” exception in a service

不打扰是莪最后的温柔 提交于 2019-12-22 18:29:11

问题


We are using the WPF FormattedText object to determine text size in a service that grabs the latest news headlines from an RSS feed. The text retrieved needs to be in a specified canvas size. The service runs the code every 10 seconds and uses up to 2 threads if one takes longer than that. I'm using TaskFactory (which I've overridden the LimitedConcurrencyLevelTaskScheduler to limit to the amount of threads I specified).

This works great, except after several days (the length is variable), we start to get the following exceptions. The same code was working fine before we started using TPL to make it mult-threaded.

I need help figuring out what this is caused by. A few thoughts I'm looking into are: thread collisions holding on to a TTF file, memory issue, the dispatcher (see the stack trace) isn't playing nicely with the TaskFactory, other?? We don't have good profiling setup, but we've looked at the TaskManager when the exception is occurring and memory usage looks normal. My next attempt is to use the TextBlock object and see if the exception is avoided.

Error Message: The system cannot find the file specified Error Source: WindowsBase Error Target Site: UInt16 RegisterClassEx(WNDCLASSEX_D)

Exception Stack Trace:

at MS.Win32.UnsafeNativeMethods.RegisterClassEx(WNDCLASSEX_D wc_d) at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks) at System.Windows.Threading.Dispatcher..ctor() at System.Windows.Threading.Dispatcher.get_CurrentDispatcher() at System.Windows.Media.TextFormatting.TextFormatter.FromCurrentDispatcher(TextFormattingMode textFormattingMode) at System.Windows.Media.FormattedText.LineEnumerator..ctor(FormattedText text) at System.Windows.Media.FormattedText.DrawAndCalculateMetrics(DrawingContext dc, Point drawingOffset, Boolean getBlackBoxMetrics) at System.Windows.Media.FormattedText.get_Metrics() at (my method using the FormattedText, which is in a loop)

   private static Size GetTextSize(string txt, Typeface tf, int size)
    {
        FormattedText ft = new FormattedText(txt, new CultureInfo("en-us"), System.Windows.FlowDirection.LeftToRight, tf, (double)size, System.Windows.Media.Brushes.Black, null, TextFormattingMode.Display);
        return new Size { Width = ft.WidthIncludingTrailingWhitespace, Height = ft.Height };
    }

EDIT: so far I've tried placing a lock around the code that calls this function, and calling it inside the CurrentDispatcher.Invoke method like so:

 return (Size)Dispatcher.CurrentDispatcher.Invoke(new Func<Size>(() =>
    {
      FormattedText ft = new FormattedText(txt, new CultureInfo("en-us"), System.Windows.FlowDirection.LeftToRight, tf, (double)size, System.Windows.Media.Brushes.Black, null, TextFormattingMode.Display);
       return new Size { Width = ft.WidthIncludingTrailingWhitespace, Height = ft.Height };
      }));

EDIT: I've found links to others having similar, but not the exact problem. http://www.eggheadcafe.com/software/aspnet/31783898/problem-creating-an-bitmapsource-from-an-hbitmap-in-threaded-code.aspx ~having a similar problem, but no answers

System.Windows.Media.DrawingVisual.RenderOpen() erroring after a time ~having a similar problem, but no answers

http://connect.microsoft.com/VisualStudio/feedback/details/361469/net-3-5-sp1-breaks-use-of-wpf-under-iis# ~ similar exception, but we're not using 3.5SP1 or IIS 7.

I've also submitted this through the Microsoft Connect site (please vote for it if you are having a similar problem). https://connect.microsoft.com/WPF/feedback/details/654208/wpf-formattedtext-the-system-cannot-find-the-file-specified-exception-in-a-service

EDIT: Response from Microsoft: "WPF objects need to be created on Dispatcher threads, not thread-pool threads. We usually recommend dedicating a thread to run the dispatcher loop to service requests to create objects and return them frozen. Thanks, WPF Team" ~ How would I implement this?

EDIT: final solution thanks to NightDweller

if(Application.Current == null) new Application();
(Size)Application.Current.Dispatcher.CurrentDispatcher.Invoke(new Func<Size>(() =>
        {
...});

EDIT: When I deployed the change (new Application();), I got an error logged " Cannot create more than one System.Windows.Application instance in the same AppDomain." Error Source: PresentationFramework Error Target Site: Void .ctor()


回答1:


As you already know from the info you provided, All UI elements (FormattedText is one) have to be created on the UI thread.

The code you are looking for is:

return (Size)Application.Current.Dispatcher.CurrentDispatcher.Invoke(new Func<Size>(() =>
    {
      FormattedText ft = new FormattedText(txt, new CultureInfo("en-us"), System.Windows.FlowDirection.LeftToRight, tf, (double)size, System.Windows.Media.Brushes.Black, null, TextFormattingMode.Display);
       return new Size { Width = ft.WidthIncludingTrailingWhitespace, Height = ft.Height };
      }));

Notice the Application.Current - you want the "Application" dispatcher which is the dispatcher for the UI thread in WPF applications. Your current code actually creates a dispatcher for the current thread so you didn't really change the executing thread (see here regarding the dispatcher)




回答2:


A shot in the dark:

The stack trace seems to show that WPF does not find a Dispatcher in the thread executing GetTextSize, so it has to create a new one, which involves creating a handle to a window.

Calling this every 10 seconds means 8'640 threads, thus windows per day. According to Mark Russinovich, there is a limit of 32 K windows per session, which may explain the error in RegisterClassEx.

An idea to overcome this is to read the current dispatcher from your main thread and set it in your tasks.

Edit: I had another look and it looks like one cannot set the Dispatcher of a thread (it's created automatically).

I'm sorry, I am unable to understand what is going on here.

In order to compute the text size, WPF needs a FormattedText instance, which is stored as a member of the Dispatcher class. The existing Dispatchers are stored in a list of weak references. Each one is associated with a specific thread. Here, it looks like new Dispatcher instances are created many, many times. So, either the calling thread is new or memory is quite low and the weak references have been discarded. The first case (new thread) is unlikely as the task scheduler uses the thread pool, which has about 25 threads per core (if I remember correctly), which is not enough to deplete the pool of ATOMs or windows. In the second case, the depletion of resource is unlikely as the HwndWrapper is IDisposable and the Dispose method takes care of freeing the registered class.




回答3:


Have you renamed anything? If yes, check that link: WPF Prism: Problem with creating a Shell



来源:https://stackoverflow.com/questions/5195808/wpf-formattedtext-the-system-cannot-find-the-file-specified-exception-in-a-ser

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!