WPF FormattedText «Системе не удается найти указанный файл» исключение в службе - PullRequest
1 голос
/ 04 марта 2011

Мы используем объект WPF FormattedText для определения размера текста в службе, которая получает заголовки последних новостей из RSS-канала.Полученный текст должен быть в указанном размере холста.Служба запускает код каждые 10 секунд и использует до 2 потоков, если один занимает больше времени.Я использую TaskFactory (который я переопределил LimitedConcurrencyLevelTaskScheduler, чтобы ограничить количество потоков, которые я указал).

Это прекрасно работает, за исключением того, что через несколько дней (длина переменная), мы начинаем получатьследующие исключения.Тот же код работал нормально до того, как мы начали использовать TPL, чтобы сделать его многопоточным.

Мне нужна помощь, чтобы выяснить, чем это вызвано.Несколько мыслей, которые я изучаю: коллизии потоков, удерживающие файл TTF, проблема с памятью, диспетчер (см. Трассировку стека) не очень хорошо играет с TaskFactory, другие ??У нас нет хорошей настройки профилирования, но мы посмотрели на TaskManager, когда возникает исключение и использование памяти выглядит нормально.Моя следующая попытка - использовать объект TextBlock и посмотреть, будет ли исключение исключено.

Сообщение об ошибке: системе не удается найти указанный файл. Источник ошибки: Целевой сайт ошибки WindowsBase: UInt16 RegisterClassEx (WNDCLASSEX_D)

Трассировка стека исключений:

в MS.Win32.UnsafeNativeMethods.RegisterClassEx (WNDCLASSEX_D wc_d) в MS.Win32.HwndWrapper..ctor (Int32 classStyle, стиль Int32, Int32 exStyle, Int32 x, Int32 y,ширина, высота Int32, имя строки, родительский элемент IntPtr, хуки HwndWrapperHook []) в System.Windows.Threading.Dispatcher..ctor () в System.Windows.Threading.Dispatcher.get_CurrentDispatcher () в System.Windows.Media.TextFormatting.TextFormatter.FromCurrentDispatcher (TextFormattingMode textFormattingMode) в System.Windows.Media.FormattedText.LineEnumerator..ctor (FormattedText text) в System.Windows.Media.FormattedText.DrawAndCalculateMetrics..FormattedText.get_Metrics () at (мой метод использует FormattedText, который находится в цикле)

   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: до сих пор я пытался установить блокировку вокруг кода, вызывающего эту функцию, и вызвать его внутриCurrentDispatcher.Invoke метод, например, так:

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

РЕДАКТИРОВАТЬ: я нашел ссылки на других, имеющих похожие, но не точную проблему.http://www.eggheadcafe.com/software/aspnet/31783898/problem-creating-an-bitmapsource-from-an-hbitmap-in-threaded-code.aspx ~ есть похожая проблема, но нет ответов

System.Windows.Media.DrawingVisual.RenderOpen () выдает ошибку через некоторое время ~ возникла похожая проблема, но нетответы

http://connect.microsoft.com/VisualStudio/feedback/details/361469/net-3-5-sp1-breaks-use-of-wpf-under-iis# ~ аналогичное исключение, но мы не используем 3.5SP1 или IIS 7.

Я также отправил это через сайт Microsoft Connect (проголосуйте заэто если у вас возникла похожая проблема).https://connect.microsoft.com/WPF/feedback/details/654208/wpf-formattedtext-the-system-cannot-find-the-file-specified-exception-in-a-service

РЕДАКТИРОВАТЬ: Ответ от Microsoft: «Объекты WPF должны создаваться в потоках Dispatcher, а не в потоках пула потоков. Обычно мы рекомендуем выделять поток для запуска цикла диспетчера для запросов на обслуживание для создания объектов иверните их замороженными. Спасибо, WPF Team "~ Как бы я это реализовал?

РЕДАКТИРОВАТЬ: окончательное решение благодаря NightDweller

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

РЕДАКТИРОВАТЬ: Когда я внедрил изменения (новое приложение ();), Я получил сообщение об ошибке «Невозможно создать более одного экземпляра System.Windows.Application в одном домене приложений».Источник ошибки: Ошибка PresentationFramework Целевой сайт: Void .ctor ()

Ответы [ 3 ]

2 голосов
/ 04 марта 2011

выстрел в темноте:

Трассировка стека, кажется, показывает, что WPF не находит Dispatcher в потоке, выполняющем GetTextSize, поэтому он должен создать новый, который включает в себя создание дескриптора окна.

Вызов этого каждые 10 секунд означает 8'640 потоков, то есть окон в день. Согласно Марк Руссинович , существует ограничение в 32 K окон на сеанс, что может объяснить ошибку в RegisterClassEx.

Идея преодолеть это состоит в том, чтобы прочитать текущий диспетчер из вашего основного потока и установить его в ваших задачах.

Edit: У меня был другой взгляд, и похоже, что нельзя установить Dispatcher потока (он создается автоматически).

Извините, я не могу понять, что здесь происходит.

Чтобы вычислить размер текста, WPF необходим экземпляр FormattedText, который хранится как член класса Dispatcher. Существующие Диспетчеры хранятся в списке слабых ссылок. Каждый из них связан с определенной темой. Здесь, похоже, новые экземпляры Dispatcher создаются много-много раз. Таким образом, либо вызывающий поток является новым, либо память достаточно мала, а слабые ссылки были отброшены. Первый случай (новый поток) маловероятен, так как планировщик задач использует пул потоков, который имеет около 25 потоков на ядро ​​(если я правильно помню), что недостаточно для исчерпания пула ATOM или окон. Во втором случае истощение ресурса маловероятно, так как HwndWrapper является IDisposable, а метод Dispose заботится об освобождении зарегистрированного класса.

1 голос
/ 05 апреля 2011

Как вы уже знаете из предоставленной вами информации, все элементы пользовательского интерфейса (FormattedText равен одному) должны быть созданы в потоке пользовательского интерфейса.

Код, который вы ищете:

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

Обратите внимание на Application.Current - вам нужен диспетчер «Application», который является диспетчером потока пользовательского интерфейса в приложениях WPF. Ваш текущий код фактически создает диспетчер для текущего потока, поэтому вы действительно не изменили исполняющий поток (см. здесь относительно диспетчера)

0 голосов
/ 04 марта 2011

Вы переименовали что-нибудь?Если да, проверьте эту ссылку: WPF Prism: проблема с созданием оболочки

...