Lately, I’ve been receiving a number of Ankh error reports that had an AccessViolationException. This was particularly weird because you cannot get that exception from safe managed code. AccessViolationException is the exception you get when you try to access memory you’re not allowed to access, usually because you are dereferencing an invalid pointer. Since safe managed code does not expose raw pointers, it’s not an exception you usually see in .NET development.
As it turned out, the exception came from a property accessor on an object from the Visual Studio automation model, a Project object. This is a COM object exposed to .NET and is implemented by the implementor of a specific project type, such as a C# project or a C++ project. Being a COM object, it can, of course, be implemented in a non-managed language, usually C++, which I suspect is the case here.
That answered the question of why such an exception could be caught inside Ankh, but it did not answer why we were seeing it in this particular case. It took a while for me to be able to reproduce it, but one submitter of an error report finally gave me a reproducible recipe: he had clicked on a linker error message in the Visual Studio error list. Linker errors aren’t associated with a specific source file, so clicking it usually does you no good, but in this case, it was clear that Ankh was doing something in response to this clicking and in the process, triggering the exception.
All files opened in Visual Studio belong to a project. This holds true even if you open a file that’s not part of your current solution, or if you don’t have a solution open. In that case, Visual Studio has a special project called "Miscellaneous Items", into which all files not part of any other project are put. This project is usually not shown in the solution explorer, but you can enable it by checking the "Show Miscellaneous files in Solution Explorer" checkbox in Tools->Options->Environment->Documents.
This project is not always present; rather, VS will load this project when the first "miscellanous" file is opened and close it again when no such files remain. Ankh has code to detect when a project is loaded and unloaded, and when a project is unloaded, we hook up an event sink to handle the case where files are added to or removed from the project, so we can refresh the status icons in the solution explorer. However, when such an event is raised, we cannot perform the refresh right away, because for some weird reason, the object model for the items hasn’t been updated to account for the changes yet. What we chose to do here is to schedule a delayed refresh of the item in question; ie, perform a refresh after a specific amount of time after the event.
What happened in this case was this:
- User clicked on the linker error in the error list.
- This, for some reason, triggered Visual Studio to load the Misc. Items project.
- Ankh then dutifully hooked itself up as an event sink for events on the Misc. Items project
- Visual Studio would then trigger the "Item added" event for the Misc. Items project (I’m not sure what it actually added, and don’t think I’ll spend much time trying to investigate it).
- Ankh would then schedule a delayed refresh of the project.
- Visual Studio would then, probably because it suddenly realized there was nothing to show, unload the Misc Items project again.
- Ankh would unhook itself as an event sink for the Misc Items project.
- However, the delayed refresh would still fire after its given interval.
- The refresh would then work against a Project object representing a project that was no longer loaded. Accessing its properties at this time triggers the AccessViolationException.
The above sequence is a little simplified – Visual Studio did actually fire several Item Added and Item Removed events before unloading the Misc Items project, but it illustrates the problem.
One unanswered question here is why the COM/.NET infrastructure allowed the Project object to become invalid when there was still a reference to it in the managed heap. We’ve had a similar problem with ProjectItem objects, but in that case we got a more informative exception stating "ProjectItem not available".
Oh well, now that I know what the problem is, I only have to find a solution 🙂