I ran into a couple if issues with the VS.NET add-in in Help Builder today that needed fixing. The issue is that on Interfaces the two way tools that let you pick up XML Comments and sync them with topics in the help file wasn’t working.

 

Inside of the Add-In there’s code that picks up the current CodeElement and walks it back to the class,member level, picks up the item’s signature and tries to look it up with what’s in Help Builder. This works fine with classes, properties, fields, methods and events. However, today I realized it does not work Interfaces, Enums or any of their members.

 

The reason for this is that each CodeElement requires some special parsing and there are separate methods that handle parsing out information for each type of element. The conditional looks like this (where element is the selected CodeElement):

 

if (element.Kind == vsCMElement.vsCMElementFunction)

      Helper.UpdateHelpBuilderMethodFromSourceCode ((CodeFunction) element);

else if (element.Kind == vsCMElement.vsCMElementProperty )

      Helper.UpdateHelpBuilderPropertyFromSourceCode( (CodeProperty) element);

else if (element.Kind == vsCMElement.vsCMElementVariable)

      Helper.UpdateHelpBuilderVariableFromSourceCode( (CodeVariable) element);

else if (element.Kind == vsCMElement.vsCMElementEvent )

      Helper.UpdateHelpBuilderEventFromSourceCode( (CodeVariable) element);

 

// *** Classes,interfaces,enums,delegates

else if (element.Kind == vsCMElement.vsCMElementClass  )

      Helper.UpdateHelpBuilderClassFromSourceCode( (CodeClass) element );

 

This is very messy code as it is because each type of CodeElement needs a special handler because each needs to parse out different information. Even things like Property and Field require separate methods because CodeProperty and CodeVariable can’t be cast to each other and have no common interface that can be used to generically retrieve the data. The result is – lots of duplication of code. This is one of the few places where strong typing really gets in the way! This means there are two methods (three really with the Events code) with nearly identical code, the only difference being the parameter passed in.

 

My hope was that Interfaces, Enums and Delegates would get picked up as CodeClass, but the same damn problem happens there, so there are three nearly identical parser methods of that as well. Altogether there are 7 parser methods with very similar code. If somebody knows a better way I would love to hear it.

 

Another thing I ran into as I parsed this code is that Interfaces and Classes handle the Parent property differently. The Parent property is scoped to a type of CodeClass in properties and fields and the following actually works fine with class properties, fields and events:

 

CodeClass par = (CodeClass) prop.Parent;

Method.cDeclaringType = par.Name;

 

However, the same code fails with Interfaces because with Interface prop.Parent still casts to CodeClass even in the Interface class when it probably should cast to CodeInterface.

 

If fact, with an Interface property any sort of access – no casting involved – fails. Even this bombs out:

 

if (prop.Parent == null)

 

Any access to the Parent property blows up on the CodeInterface class.

 

This type of inconsistency in the Add-In interface has been driving me crazy in developing this code. The model is very, very powerful but there are many little holes and oddities that make development in this space very difficult resulting in lots of specialty code and redundant methods. Yuck…

 

Luckily for me the above problem had a somewhat easy solution because all I really need was the name of the parent interface or type which can be parsed out of the Fullname property by walking back one level from the last name:

 

Method.cDeclaringType = prop.FullName;

int At1=Method.cDeclaringType.LastIndexOf('.');

int At2=Method.cDeclaringType.LastIndexOf('.',At1-1);

Method.cDeclaringType = Method.cDeclaringType.Substring(At2+1,At1-At2-1);

 

This is a hack for sure and the parent property would be more reliable, but this works in all members regardless of the declaring type. But, if I really needed a reference to the parent CodeElement I’d have to walk the hierarchy in the document to get it. Grr….