Do you remember Visual Studio 2003 and its nice feature of generating HTML documentation for your projects? I bet many of you do and just like I do, you miss it now because unfortunately in Visual Studio 2005 it's no longer present.
Recently, I needed such functionality to document a .NET library I've been developing and just couldn't find the right tool to do the job. I'm aware of the existence of NDoc, but as far as I know it's quite dormant these days and it lacks support for .NET Framework 2.0. So I decided to spare some time to develop such a tool by myself, hoping that I would learn something new and that it'll be useful not only for me, but also for other programmers out there.
Below I put the list of topics that I had to broaden my knowledge on to complete the project, so you know what you can learn by analyzing the provided source code:
- reflection mechanisms
- subtleties of C# and MSIL
- regular expressions
- syntax of the XML comment files generated by the compiler
- how to read XML files and programmatically validate them against a schema definition
- embedded resources
Before I give you more technical details about ImmDoc .NET, you may want to visit this page and see the example output generated by this tool. That is, documentation of three arbitrarily chosen assemblies from .NET Framework:
Currently, there's only command-line interface available for ImmDoc .NET. One of the reasons for this is the fact that creating GUI requires some time. However, thanks to this approach one can easily use the program from batch scripts or tools supporting the build process, such as NAnt.
The usage of ImmDoc .NET is pretty easy and straightforward. You can put all of your assemblies and XML comment files in one folder along with the ImmDoc .NET executable. Then run the program and, after some processing, you'll get a
doc folder containing the generated documentation. You can also explicitly give the names of the files you want ImmDoc .NET to process and you can use a few options, which I list below:
-pn, -ProjectName:STRING- use this switch if you want to give your documentation project a meaningful name
-ex, -Exclude:FILE- if you don't explicitly give the file names, you can use this switch to exclude particular files from processing
-od, -OutputDirectory:DIR- use this switch to specify the name of the directory where you want to have your documentation generated
-fd, -ForceDelete - ImmDoc.NET will not delete the output directory if it already exists, unless you use this switch
-vl, -VerboseLevel:LEVEL- you can set various levels of verbosity, i.e. the greater the level is, the more output from the program you'll get; LEVEL can be from 0 (no output) to 3 (full output)
There is also a feature which needs explicit mention. Because it's not possible to put XML comments on namespaces and because you can document multiple assemblies together, ImmDoc .NET provides a way to add additional comments. To do this, you have to create an XML file with the .docs extension. The syntax of this file is very simple, but note that it'll be validated against a schema, namely AdditionalDocumentation.xsd, which you can find in the ZIP archive with the source code. After you've created such a file, all you have to do is put it in one folder with your assemblies and XML comment files that you want to process or just explicitly give its name as an argument to the program. Below is the example:
Description of the assembly goes here.
Description of the namespace goes here.
Project architecture and design
ImmDoc .NET comprises two assemblies that are merged at the end by the ILMerge tool, which is why there's only one EXE file. These are ImmDocNetLib, which actually does the whole job of analyzing assemblies and XML comment files, and ImmDocNet, which is a console application that uses ImmDocNetLib and provides the interface for the user.
As I've already said, ImmDocNetLib has two main responsibilities: analysis of given assemblies and generating the documentation. Let's have a closer look at the first task, which in principle involves using reflection to obtain detailed information about assemblies and the modules they contain.
You'll find the most important classes in the
Imm.ImmDocNetLib.MyReflection.MetaClasses namespace. These classes are lightweight and more or less exact equivalents of classes from the
System.Reflection namespace. Essentially, almost all of them inherit from the
MetaClass class which, among other things, contains properties like
Remarks. Other classes add specific details concerning entities they represent, e.g..
MyMethodInfo holds information about return type and parameters. Such information is often represented in ImmDoc .NET by other classes that inherit from
MetaClass like, for example,
So in the first step of the processing, information obtained using ordinary reflection mechanisms is combined with comments extracted from XML comment files to form an internal representation of given assemblies. Below is the UML diagram, which will hopefully give you some overview of this representation. To avoid clutter, not all classes are included:
After collecting all of the needed information, it's finally time to start generating some documentation. Relevant classes are contained in the
Imm.ImmDocNetLib.Documenters namespace. We have there a simple abstract class named
Documenter, from which we can derive other classes responsible for generating documentation in whatever format we like. I've implemented the
is a rather big class, so in order to reuse some functionality one would probably want to do some high-level refactoring before implementing one's own documenter. To give you a general overview of the responsibilities of
, I've created a simple UML diagram that lists example methods implemented by this class.
Here are short descriptions of the above methods:
bool GenerateDocumentation(string outputDirectory,
This initiates the process of generating documentation. It invokes such methods as
MyClassInfo declaringType, string dirName)
This method creates an HTML page that will contain an index of all overloads of a particular method or constructor.
string CreateNamespaceMemberSyntaxString(MyClassInfo namespaceMember)
This method is responsible for creating a type declaration string. Such a string comprises, for example, a visibility modifier, base class, implemented interfaces, constraints on generic parameters, etc.
void ExtractBinaryResourceToFile(string resourceName, string fileName)
There are couple of graphics used in the generated pages. This method is used to extract a specified embedded resource to a physical file.
string ProcessComment(string contents)
This method processes every comment found in an XML file in order to replace such tags as
<see> and so on with appropriate HTML tags.
void WriteIndexHeader(StreamWriter sw, string pageTitle,
This method writes a common HTML header used by almost all generated pages.
string ResolveLink(MetaClass metaClass)
For the generated documentation to be usable, it is necessary to provide some navigation between pages. This method aids with the task of creating hyperlinks, for example, to the particular member of a class.
I hope that someone will find ImmDoc .NET useful; I know I do. I'm waiting for some feedback and if there is some interest I'll continue to develop this project. It still lacks some minor features and, after all, a user-friendly front-end would be a nice thing to have. So if you have some suggestions, questions or maybe you've found a bug I'd be more than glad to "hear" from you. My e-mail is: [admin at immortal dot pl]. You may also want to visit the ImmDoc .NET Homepage. This is the place where you'll most probably find the latest releases of the project and news concerning it.
- 23 May, 2007 -- Original version posted
- 21 June, 2007 -- Downloads updated
- 27 June, 2007 -- Downloads updated