Phillip Trelford's Array

POKE 36879,255

Random Walker

This week our automated tools uncovered a fatal exception before it reached the client. Nick and Anton, 2 of the full-time testers on our team have written a testing tool they call Random Walker that simulates interactions with the user interface as clicks and gestures. It has helped uncover numerous issues, particularly NullReferenceExceptions, more often than not emanating from third-party libraries. It typically finds issues that are not easily found by manual testing or our extensive suite of unit and integration tests. Up-time is very important to us and our large client base who interact with our software throughout the day. 

Stack Trace

System.InvalidOperationException: GridViewColumnCollection is read-only now.
 at System.Windows.Controls.GridViewColumnCollection.VerifyAccess()
 at System.Windows.Controls.GridViewColumnCollection.RemoveItem(Int32 index)

Documentation

The exception is thrown after calling RemoveItem method of GridViewColumnCollection, although the MSDN documentation does not specify an exception is expected.

Disassembly

Using a disassembler we can see VerifyAccess will throw if the IsImmutable property is true:

private void VerifyAccess()
{
    if (this.IsImmutable)
    {
        throw new InvalidOperationException(
                      SR.Get("ListView_GridViewColumnCollectionIsReadOnly"));
    }
    base.CheckReentrancy();
}

 

The next question is when does this property get set to true. Using Telerik’s JustDecompile you can right click the property and select Find Usages, which shows its set from methods BlockWrite and UnblockWrite.

BlockWrite is called by the GridViewHeaderRowPresenter.StartHeaderDrag private method.

UnblockWrite is called by GridVIewHeaderRowPresenter.FinishHeaderDrag.

StartHeaderDrag is called from the MouseMove handler.

FinishHeadDrag is called from OnMouseLeftButtonUp, OnLostMouseCapture and OnColumnsPresenterKeyDown.

Problem

There appears to be an undocumented assumption that the columns can not be updated either during or on completion of a drag operation. However our front end lets you add or remove columns from a table via a separate menu, so it is possible for an update operation to come from outside the drag gesture.

Workaround

One workaround is to pre-empt the call to VerifyAccess by checking the IsImmutable property as it is possible to invoke private methods via refection:

bool IsImmutable()
{
    var flags = BindingFlags.Instance || BindingFlags.NonPublic;
    var property = columns.GetType().GetProperty("IsImmutable");
    return (bool)property.GetValue(columns, new object[] {});
}

 

Closing Thoughts

The Random Walker UI testing tool has really helped flush out those hard to find issues, if up-time is important to your customers, you may want to consider using one. Bugs exist even in mature frameworks like WPF, and you may need to set aside some time to working around them. Encapsulation seems good in theory, but I’m thankful that we can disassemble the source of third-party libraries and dynamically invoke private members when necessary.

Comments are closed