[LibDocu]

Introduction

Wednesday, March 28, 2001

LibDocu is an ActiveX-EXE that helps you document your VB6 projects. You can use it in several ways (as I'll discuss). It's source code-oriented (it does not depend on TlbInf32.dll), and there's an object model built arround Visual Basic's programming elements such as classes and enums.

You can download the source code and the binary. Please be sure to read the VB6Core topic for a discussion about builds, dependencies, and compatibility. What's more, LibDocu depends on the VB6Core component (you can download that one from the VB6Core topic). However, LibDocu has no other direct dependencies.

In the zip file, there are same ".ldt" files (LibDocu templates). Copy them to an extra folder. Also, there are some GIFs and a StyleSheet; keep them in an extra folder as well, and copy them to the output folder after the program has finished documenting a project.

Basic concepts

LibDocu reads the .vbp file (that's the project file), and follows the included code files. However, I have restricted it to classes because documentation is most crucial here. For example, enums and structures have to be defined in (public) classes anyway in order to be public. Mostly, you'll want to use LibDocu for ActiveX-DLLs or ActiveX-EXEs. However, it shouldn't be that hard to add the parsing code for bastard modules if you really want to.

As LibDocu parses the source code, there's no need for TlbInf32.dll. The latter allows you to access the code model of a type library. The perfect example is the VB Object Browser, which uses this component to display the interfaces of referenced referenced libraries. The downside of not using TlbInf32.dll is the obvious bug potential and the loss of some features. The good news is that source code analysis lets you read code comments (other than member descriptions), making documentation a coding-time activity. LibDocu does not implement this feature yet, but I might work on it in a moment of classic VB nostalgia.

If you use LibDocu as an executable, it will generate files, mostly in HTML format, but that's really up to you. You can control both the structure as well as the formatting by providing custom templates. I'll discuss this below.

You can use LibDocu as a component, too. For example, you can hook it up in Access and write the results to a database. You'll find some hints towards the end of this page.

Coding requirements

Some of the parsing code is plain crazy. Just try to follow the parts of the CClass class that deal with properties. I use the NList class from VB6Core a lot (it's a real helper if you read from files). But the code is readable, and you can add custom parsing code if you require it. In any case, it's not Perl.

Now, there are some restrictions. If you're one of those fellows that indent procedure signatures, LibDocu won't recognize them. The same goes for the opening statement of a structure or an enum (but indenting in between the block is OK).

LibDocu does recognize, however, line continuation. That's because I use it sometimes, so I allowed for it. My tests show it's reliable.

Using the program

From a usage standpoint, there isn't much to LibDocu. Select the project file, say what kinds of types you want to include in the documentation, pick a template, specify an output folder and the file extension, and hit "Create":

Now, lean back and relax as the wizard
creates the documentation!

In the statusbar, you can see the progress. LibDocu creates one or more files with your project's documentation; you can control how the documentation mappes to physical files in the template file. The latter also controls the formatting (binding in stylesheets or image references). But there's a lot more to the templates, as you shall soon find out.

If you use a template from the zip file, you'll probably need the resources it includes as well. Copy them to the output folder when the program has finished.

Structuring a library

LibDocu thinks that documentation should be structured into five levels. These correspond to programming elements, although there are more classes in LibDocu's object model than that. For example, a class (CClass class) is at the same level as a structure (CStructure class). Here's how your code will be organized:

These levels are important, because they interact with template files, controlling how many HTML files are created. You can have anything from one just file for the whole library to one file for every method parameter.

Enter the object model

The classes that represent code elements are organized as follows:

CProject
NClassesNStructuresNEnums
CClass/NMembersCStructure/NElementsCEnum/NConstants
CMember/NParametersCElementCConstant
CParameter

I think the object model is easy to follow from just reading the names. You can use the object browser to find out more. Note that the logical levels here do not necessarily correspond to object model hierarchies. Just for the sake of nitpicking, I hereby state that an object model is not the same thing as a class hierarchy, but of course you know that.

Enter the template

You use a template to tell LibDocu how to distribute all the text it generates among files. The LibDocu template language, which I hereby introduce, has special keywords recognized by the program, which define a "section" of the documentation. A section mostly denotes a programming element, such as a class. But there can be more than one section for a given programming element (such as opening and closing sections), while some sections do not strictly correspond to any programming element.

Basicly, you put such a keyword into the template, and LibDocu will include the text following it in the output. These keywords are preceded by special tokens (called section modifiers), which tell the program whether a section goes into its own file or into the parent file.

Sections, keywords and levels

Here is a list of those keywords, ordered by those five levels mentioned above. The third column tells you which kinds of objects are actually used for the respective level:

LevelKeywordsInstance of
ProjectStartProject, EndProjectCProject
Type overviewStartClasses, EndClasses
StartStructures, EndStructures
StartEnums, EndEnums
CProject
CProject
CProject
TypeStartClass, EndClass
StartStructure, EndStructure
StartEnum, EndEnum
CClass
CStructure
CEnum
MemberStartMember, EndMember
Element
Constant
CMember
CElement
CConstant
ParameterParameterCParameter

Note that you can access the CProject instance at the type overview level instead of the collection classes. I deemed it not necessary to access the collection classes, since the CProject instance has properties that return them. However, I have not yet implemented a syntax that lets you access properties that return objects. It might be interesting to get at the Count property of the collection, but not this time.

Here is an example of how to use the keywords:

#StartProject
<html>
<head>
<title>Project: $Name%</title>
</head>
<body>
#

@StartClass
<p>
Class $Name%: $Description%
</p>
@

#EndProject
</body>
</html>
#

This example tells a lot about how the LibDocu template language works:

Inside sections, there can be literals, variables, and conditional expressions.

Literals

Let's consider this section, which is written for every class member parameter:

@Parameter
<p>
This paragraph is written "as is" (including HTML tags).
</p>
@

The LibDocu template language is line oriented, meaning that a template is translated line by line. Line breaks are inserted into the output files as they stand in the template. It also treats everything as a literal, except keywords introduced by section modifiers (see above), variable accessors, the variable names in between variable accessors, and conditional operators.

Variables

You can access properties of the object that is available for the current section. See above for a table that maps section keywords to objects. For example, in the "StartMember" or "EndMember" section, you can access a CMember object. An object whose properties you can read in a section is called an accessible object with respect to that section.

You can only access properties with no parameters. The data type must be a primitive type (not a complex type or a reference type). The return value will converted into a String; beware of VB's output formatting.

Use the "$" sign at the beginning of a property name (opening variable accessor); use the "%" sign at the end of the name (closing variable accessor). You need to end the variable on the same line where it begins. You can use several variables in one line, however. In every line, the number of "$" signs must equal the number of "%" signs, otherwise it is erroneous. A line with a syntax error will be replaced by a blank line in the output. Here is an example:

#StartClass
Name of the current class: $Name%
#

Conditional expressions

You can add conditional expressions to a template, modifying the output according to the value of a property of an accessible object. Like variables, you use the "$" sign to open a conditional expression, and a "%" sign to close it. Both tokens must occur the same number of times in a line; a conditional expression cannot exceed one line. Syntax errors are handled by outputting a blank line.

In a conditional expression, there is the Test part, the True part, and an optional False part. The test part ends with a question mark ("?"), and includes exactly one of several comparison operators. The following example will add the text "GlobalMultiUse" if the Instancing property of the accessible CClass object is six:

$Instancing=6?GlobalMultiUse%

Note that the literal "GlobalMultiUse" is not enclosed in quotation marks. Likewise, if you test for a certain string, you do not enclose it in quotes; the following example uses the pattern-matching operator "~":

$Name~I*?Interface class%

Here is a list of comparison operators recognized by LibDocu:

As with variables, one line may contain several conditional expressions:

$Instancing=2?PublicNotCreatable%$Instancing=4?GlobabSingleUse%

You can specify a False part. In this case, use the ":" token to seperate it from the True part. This example assumes it's in the "Parameter" section, accessing an instance of CParameter:

$Mode=1?ByVal:ByRef%

Both True and False parts may contain literals, variables, or a combination of both:

$MemberType=1?Function $Name%:$Name%%

You can also nest conditional expressions, opening a new conditional block inside the True part, the False part, or both. Think of the "$" and "%" sign as parens:

$Name~I*?Interface:%Name~N*?Collection class%:$Name~G*?Global class%%%

The next example shows how you can use parens (the "$" and the "%" signs) to arrive at a different behaviour:

$Name=GUtilities?$Instancing=4?GlobalSingleUse:GlobalMultiUse%%
$Name=GUtilities?$Instancing=4?GlobalSingleUse%:GlobalMultiUse%

In the first line, "GlobalMultiUse" is put out only if the Name is "GUtilites" and the Instancing property is *not* equal to 4. In the second line that's the case if the Name is *not* "GUtilities", regardless of the Instancing property.

There is a little more to the Test part of the expression. You cannot use a logical "And" there; you can only compare one property at a time. However, using nested conditional expressions, you can emulate a logical "And" (or a "Between").

A logical "Or", on the other hand, is supported. Use the "|" operator. But note that the operator precedence is different from the usual, in order to allow for shorter notation and faster interpretation: The "|" (Or) operator has higher precedence that all comparison operators:

$Instancing=4|6?Global class%

In the Test part, on the right side of the comparision operator you can use literals (including blanks), but no variables or nested conditional expression. On the left side of the comparison operator, you can use a variable only.

Other ways to use LibDocu

Using ILibUser and CLibWalker

This approach gives you more control over the output, while the overall structure is the same as when using the program, relying on CLibWalker's path through the library. Implement the ILibUser interface, create an instance of CLibWalker, pass a reference of the implementing object to the ConstructObject method, and handle the callbacks yourself.

This way, you can generate custom XML output, or write to a database. You could set up a network help system, too, and generate output on demand.

Using the object model

You can also walk through LibDocu's object model yourself, for maximum flexiblity. Just create the objects as needed, and iterate over the collections.