Skip to content

Heuristic to find the parent of a projectItem used when searching for the .licenseheader file in custom project types

FlorianDecker edited this page Mar 6, 2017 · 1 revision

When the LicenseHeaderManager tries to add a license header to a file, it has to search for a .licenseheader file first. As the .licenseheader file can be located at a folder above the current file, whe iterate UP in the solution directory.

In default projects of Visual Studio, this is achieved by calling projectItem.Collection.Parent, which returns the parent of the current file and enables us to iterate up.

However, Visual Studio also supports custom project types. These custom project types sometimes do not behave like the default project types. A common problem we faced with some of these project types, was that projectItem.Collection.Parent did not return the parent of the projectItem, but the projectItem again. This led to an endless recursion and a StackoverflowException - crashing Visual Studio.

To fix this, we can use the custom object supplied by whoever created the custom project type (projectItem.Object). As this object can have any type, we have to search for the value we need via reflection. In the currently observed cases, the custom object always has a Property "Parent". This object has a property "Url", which gives us the file path to the parent item.

We then use the DTE.Solution.FindProjectItem(string url) method, to get the desired parent project item as ProjectItem object. If we cant find the url this way, we are currently in the top layer and the only item above us is the project file (accessed by projectItem.ContainingProject).

This behaviour is tested with following projects: FsProj (Visual F# Project) WixProj (Wix Project http://wixtoolset.org/releases/ ) PHPTools (https://www.devsense.com/)

To find out which method we have to use, to find the parent (projectItem.Collection.Parent or reflection), we check if the ProjectItem returned by projectItem.Collection.Parent is equal to the currently observed projectItem. Sadly, a simple equality check is not enough, so we compare the FullPath (received via projectItem.Properties.Item("FullPath")) of the projectItems. If they are equal we have to use reflection to get the correct parent.

Introduced in 1.7.2 and refined in 1.7.3