Search code examples
c#wpftouch

How can ItemPeersStorage throw exception when getting value from WeakDictionary?


I got the following exception through automated exception reporting so I do not have much context. Looks like the application showed a view with few controls, a DataGrid being the only ItemsControl. The application is using a touch screen and I assume the tabtip.exe might cause the call into the AutomationPeer because I am not using any UI automation explicitly. The exception seems to have occurred after at least 30 minutes without any user interaction.

I'm targeting .NET Framework 4.7.2 but the computer has .NET Framework 4.8 installed.

Anyone seen this before? Any hints on why this would happen?

Unhandled exception occurred. Origin=System.Windows.Dispatcher.CurrentDispatcher.UnhandledException:
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
   at MS.Internal.WeakDictionary`2.get_Item(TKey key)
   at System.Windows.Automation.Peers.ItemPeersStorage`1.get_Item(Object item)
   at System.Windows.Automation.Peers.ItemsControlAutomationPeer.GetPeerFromWeakRefStorage(Object item)
   at System.Windows.Automation.Peers.ItemsControlAutomationPeer.AddProxyToWeakRefStorage(WeakReference wr, ItemAutomationPeer itemPeer)
   at System.Windows.Automation.Peers.ItemAutomationPeer.AddToParentProxyWeakRefCache()
   at MS.Internal.Automation.ElementProxy.StaticWrap(AutomationPeer peer, AutomationPeer referencePeer)
   at System.Windows.Automation.Peers.AutomationPeer.UpdateChildrenInternal(Int32 invalidateLimit)
   at System.Windows.Automation.Peers.AutomationPeer.UpdateChildren()
   at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
   at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
   at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
   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.FireInvokeOnRenderCallbacks()
   at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
   at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)

The System.Windows.Automation.Peers.ItemPeersStorage1.get_Item(Object item) method from From .NET Framework 4.8 Reference Source i'm not seeing how this could happen? It checks ContainsKey() before accessing the WeakDictionary.

public T this[object item]
{
    get
    {
        if (_count == 0 || item == null)
            return default(T);

        if (_usesHashCode)
        {
            if (_hashtable == null || !_hashtable.ContainsKey(item))
                return default(T);

            return _hashtable[item] as T;
        }
        else
        {
            if (_list == null)
                return default(T);

            for (int i = 0; i < _list.Count; i++)
            {
                KeyValuePair<object, T> pair = _list[i];
                if (Object.Equals(item, pair.Key))
                    return pair.Value;
            }

            return default(T);
        }
}

Solution

  • I downloaded the code for .NET Framework 4.7.2 from Reference Source and it seems like there is a bug in that version because they are NOT checking ContainsKey():

    get
    {
        if (_count == 0 || item == null)
            return default(T);
    
        if (_usesHashCode)
        {
            if (_hashtable == null)
                return default(T);
    
            return _hashtable[item] as T;
        }
        else
        {
            if (_list == null)
                return default(T);
    
            for (int i = 0; i < _list.Count; i++)
            {
                KeyValuePair<object, T> pair = _list[i];
                if (Object.Equals(item, pair.Key))
                    return pair.Value;
            }
    
            return default(T);
        }
    }
    

    Since I'm targeting .NET Framework 4.7.2 and using the <supportedRuntime/> configuration element I am not hitting the .NET Framework 4.8 code, which I assumed at first.

    So, bug fixed in .NET Framework 4.8. I guess I'll target that version instead.