Исключение NullReference из PresentationFramework.dll

Я сражаюсь со странным исключением NullReferenceException, которое, по-видимому, запущено из функции GetNameCore() ItemAutomationPeer.

Подробности исключения приведены ниже, но действительно интригующий аспект заключается в том, что он не возникает на моей машине разработки под управлением Windows 7 или других компьютерах под управлением Windows 7, с которыми мы тестировали. Это происходит только на тестовой машине Windows 8 Pro.

Исключение возникает, по-видимому, при попытке редактировать ячейку в элементе управления WPF DataGrid.

Я пытался отслеживать его весь день без успеха. Я попытался удаленно отлаживать процесс с помощью Visual Studio и переходить через код, но ни один из кодов пользователя не запускает исключение. Это, очевидно, выполняется последовательностью событий внутри PresentationFramework.Dll, и исключение просто пузырится вверх через AppDomain и в конечном итоге сбой приложения.

Если кто-нибудь может подумать о чем-нибудь, что может вызвать это, или о способе его исправления, это действительно поможет.

Exception Type:         System.NullReferenceException
Exception Message:  Object reference not set to an instance of an object.
Method Information: System.String GetNameCore()
Exception Source:   PresentationFramework

Stack Trace
  at System.Windows.Automation.Peers.ItemAutomationPeer.GetNameCore()
  at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
  at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
  at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
  at System.Windows.ContextLayoutManager.fireAutomationEvents()
  at System.Windows.ContextLayoutManager.UpdateLayout()
  at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
  at System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork()
  at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
  at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object    resizedCompositionTarget)
  at System.Windows.Media.MediaContext.AnimatedRenderMessageHandler(Object resizedCompositionTarget)
  at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
  at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
  at System.Windows.Threading.DispatcherOperation.InvokeImpl()
  at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
  at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
  at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
  at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
  at System.Windows.Threading.DispatcherOperation.Invoke()
  at System.Windows.Threading.Dispatcher.ProcessQueue()
  at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
  at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
  at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
  at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
  at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
  at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
  at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
  at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
  at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
  at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
  at System.Windows.Threading.Dispatcher.Run()
  at System.Windows.Application.RunDispatcher(Object ignore)
  at System.Windows.Application.RunInternal(Window window)
  at System.Windows.Application.Run(Window window)
  at System.Windows.Application.Run()

Ответ 1

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

Когда я столкнулся с этой проблемой, у меня появилось нулевое знание об автоматизации пользовательского интерфейса и о том, как она поддерживается в рамках WPF. Фактически, когда AutomationPeer заставил меня думать о COM-взаимодействии по какой-то причине, поэтому я некоторое время искал неправильную проблему. Если вы читаете это и не знаете, что, возможно, запускает UI Automation, здесь и здесь может дать вам что касается того, что означает UI Automation в контексте WPF.

В моем случае выясняется, что причина, по которой приложение терпело крах на тестовой машине Windows 8, но работало отлично на моей машине разработки (и бесчисленных других компьютеров, на которых она была развернута), было то, что Windows 8 у машины было какое-то приложение доступности пользовательского интерфейса (или какой-то другой клиент автоматизации пользовательского интерфейса). Как только я начал приложение "Рассказчик" на моей машине разработки Windows 7, я смог сразу же запустить приложение.

Как только я понял проблему с корнем, я все еще не смог продолжить отладку, чтобы выяснить, какой именно элемент управления вызвал проблему, но больше чтения в Интернете, казалось, указывало на общее направление пользовательских элементов управления, и поэтому я начал процесс устранения чтобы определить, какие пользовательские элементы управления WPF были виновны. Я обнаружил два настраиваемых элемента управления: один, который расширил DataGrid, а другой, который расширил ListBox.

Наконец, решение проблемы в моем случае состояло в создании пользовательских классов, которые расширяют базовый класс ItemsControlAutomationPeer и предоставляют их как узлы автоматизации на каждом из пользовательские элементы управления, которые имели проблемы, переопределяя метод OnCreateAutomationPeer.

protected override AutomationPeer OnCreateAutomationPeer()
{
    return new ControlSpecificCustomAutomationPeer(this);
}

Если класс ControlSpecificCustomAutomationPeer может выглядеть примерно так:

public class ControlSpecificCustomAutomationPeer
    : ItemsControlAutomationPeer
{
    public ControlSpecificCustomAutomationPeer(ItemsControl owner)
        : base(owner)
    {
    }

    protected override string GetNameCore()
    {
        return "";                         // return something meaningful here..
    }

    protected override ItemAutomationPeer CreateItemAutomationPeer(object item)
    {
        return new CustomDummyItemAutomationPeer(item, this);
    }             
}

public class CustomDummyItemAutomationPeer
    : System.Windows.Automation.Peers.ItemAutomationPeer
{
    public CustomDummyItemAutomationPeer(object item, ItemsControlAutomationPeer itemsControlAutomationPeer)
        : base(item, itemsControlAutomationPeer)
    {
    }

    protected override string GetNameCore()
    {
        if (Item == null)
            return "";            

        return Item.ToString() ?? "";
    }

    protected override AutomationControlType GetAutomationControlTypeCore()
    {
        return System.Windows.Automation.Peers.AutomationControlType.Text;
    }

    protected override string GetClassNameCore()
    {
        return "Dummy";
    }
}

Ответ 2

Я решил поймать ошибку и обработать ее как "нефатальную" ошибку.

private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
    if (e.Exception.Source?.ToString() == "PresentationFramework")
    {
        e.Handled = true;
        Postgres.LogException(e.Exception, false);
        return;
    }

    HandleException(e.Exception);
    e.Handled = true;
    Shutdown();
}