Using C# and COM Interoperability to Extract from a UML tool into a Microsoft Word Document

WordFromWithClass.jpg

Figure 1 - Persits API extracted from WithClass

This is a follow up article on Using Reflection and WithClass 2000 to View the .NET System.Drawing assembly.  Note that both articles have exceedingly long titles, but I couldn't figure out an easy way to present the title to the topic in a few short phrases, hence the verbosity. This article extends the previous articles capabilities.  The previous article allowed you to choose any .NET assembly and extract the assembly into a WithClass 2000 UML diagram (without relationships) using reflection and COM Interoperability. This article allows you to read the classes contained  inside of WithClass 2000 into a Word Document using COM Interoperability for Word and COM Interoperability for WithClass and presents your classes in a kind of report. The report lists each class which is  followed by its list of attribute details and its list of operation details. Our sample only used one class. The class was brought  into WithClass by extracting the class  from the assembly called Interop.ASPEMAILLib.dll. Some of you may recognize this as  the wrapper assembly around the ActiveX Control made by Persits used to send and receive mail.  (I once needed this ActiveX control to use on a website to send e-mails for a newsletter.)  Below is a UML illustration of the class: 

WordFromWithClassUML.jpg

Figure 2 - Persits MailSenderClass imported into the WithClass UML class diagram

The previous article describes how we used C# with COM interoperability to get the diagram shown in figure 2.  Now we want to place the information that is stored in WithClass into a Microsoft Word Document so we can present a report to a manager who doesn't necessarily understand UML.  We can do this using the same COM Interoperability techniques that got us this far.

The first step in creating the Word Report in our Window Forms application is to create a Reference to Word and a Reference to WithClass.  By choosing Add Reference and clicking the  COM  tab,  we can choose both With_Class and the Microsoft Word COM references in the dialog list.  Now Visual Studio will create assembly wrappers around these COM objects for us, and we can use our Microsoft Word object and With_Class object just like any other class instance.  

We are going to create a new class called WordWCExtractor which will perform all of our data extraction from WithClass into Word.  We can create new instances of our COM objects in the constructor of this class shown in Listing 1:

Listing 1 - Creating the Instances of Word and WithClass Applications

public WordWCExtractor()
{
// create a new Word COM object with our COM wrapper and launch word
WordApp = new Word.ApplicationClass();
// create a new WithClass COM Object with our COM wrapper
WCApp = new With_Class.ApplicationClass();
// make both applications visible
WCApp.Visible = true;
WordApp.Visible =
true;
}

Now we are ready to extract data from our UML class into Word.  This is done by cycling through each class in the UML diagram, pulling out the information contained in the class, and typing it into Word. WithClass uses a set of methods to loop through its various collections: Restart, IsLast, and GetNext.  Below is the loop structure that loops through each class diagram and then in turn each class to get the data from WithClass.  Also Listing 2 shows you how to do some of the Font formatting in Word.  Font formatting  requires that you toggle the font properties such as Bold or Italic prior to typing.  After you've toggled the Font property,  any text that you insert into Word by using Selection.TypeText will be styled by the toggled settings.  Don't forget to toggle the font style properties again when you are done using them so the Font returns to its original state.

Listing 2 - Looping through classes to extract data into Microsoft Word

public void WriteAllApisToWord()
{
object fileName = "normal.dot";
object readOnly = false;
object isVisible = true;
object missing = System.Reflection.Missing.Value;
object newTemplate = false;
object docType = 0;
// Create a new word document to type into
Word.Document aDoc = WordApp.Documents.Add(ref fileName, ref newTemplate, ref docType, ref isVisible);
// Write a Title for the document in bold and centered on the page
WordApp.Selection.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
WordApp.Selection.Font.Bold = (
int)Word.WdConstants.wdToggle;
WordApp.Selection.TypeText("API of Classes Extracted from the WithClass Diagram\n\n");
// Toggle centering and bold Off
WordApp.Selection.Font.Bold = (int)Word.WdConstants.wdToggle;
WordApp.Selection.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphLeft;
// Loop through each class diagram
WCApp.ActiveDocument.ClassDiagrams.Restart();
while (WCApp.ActiveDocument.ClassDiagrams.IsLast() == false)
{
With_Class.ClassDiagram nextClassDiagram =
(With_Class.ClassDiagram)WCApp.ActiveDocument.ClassDiagrams.GetNext();
// Loop through each collection of classes in the diagram
nextClassDiagram.Classes.Restart();
while (nextClassDiagram.Classes.IsLast() == false)
{
// Write class name in Bold Italic in Microsoft Word
With_Class.Class nextClass = nextClassDiagram.Classes.GetNext();
WordApp.Selection.Font.Bold = (
int)Word.WdConstants.wdToggle;
WordApp.Selection.Font.Italic = (
int)Word.WdConstants.wdToggle;
WordApp.Selection.TypeText(nextClass.Name + "\n");
WordApp.Selection.Font.Bold = (
int)Word.WdConstants.wdToggle;
WordApp.Selection.Font.Italic = (
int)Word.WdConstants.wdToggle;
// Write Attributes and Operations into Word
WriteClassAPIToWord(aDoc, nextClass);
}
}
}

We perform similar looping through data of WithClass for both attributes and operations.  Below is the method used to extract attribute information into Word.  The method simply loops through each attribute in a particular class using again - Restart, IsLast, GetNext  in a while loop.  The attribute is obtained each time through the loop and this attribute can be used to extract various attribute properties into word such as attribute type, initial value, and visibility (Note that the UML attribute is not to be confused with the attribute intrinsic to the .NET library):

Listing 3 - Writing the class attributes to Microsoft Word

public void WriteAttribute(Word.Document aDoc, With_Class.Attribute attribute)
{
// Type the name of the attribute into Word in Italics
WordApp.Selection.TypeText("Attribute Name:\t ");
WordApp.Selection.Font.Italic = (
int)Word.WdConstants.wdToggle;
WordApp.Selection.TypeText(attribute.Name + "\n");
WordApp.Selection.Font.Italic = (
int)Word.WdConstants.wdToggle;
// Type some of the attribute properties. Each property is followed by a return character
WordApp.Selection.TypeText("Type:\t " + attribute.Type + "\n");
WordApp.Selection.TypeText("Modifier:\t " + attribute.Visibility + "\n"); WordApp.Selection.TypeText("Initial
Value:\t " + attribute.InitialValue + "\n");
WordApp.Selection.TypeText("Additional Properties:\t");
// Also type some of the Attribute indicators such as constant, static, read-only
if (attribute.IsConstant)
WordApp.Selection.TypeText("constant ");
if (attribute.IsStatic)
WordApp.Selection.TypeText("static ");
if (attribute.IsArray)
WordApp.Selection.TypeText("array ");
if (attribute.IsReadOnly)
WordApp.Selection.TypeText("read-only ");
// Put paragraph breaks after each attribute
WordApp.Selection.TypeParagraph();
WordApp.Selection.TypeParagraph();
}

The routine for extracting information from the operations of the class looks very similar.  Below is the routine for extracting operation information from WithClass into Microsoft Word.

Listing 4 - Writing the Operations to Microsoft Word

public void WriteOperation(Word.Document aDoc, With_Class.Operation operation)
{
// Write the Operation name out to Microsoft Word in Italics
WordApp.Selection.TypeText("Operation Name:\t ");
WordApp.Selection.Font.Italic = (
int)Word.WdConstants.wdToggle;
WordApp.Selection.TypeText(operation.Name + "\n");
// Write out a few operation properties such as Return Type and Visibility
WordApp.Selection.Font.Italic = (int)Word.WdConstants.wdToggle;
WordApp.Selection.TypeText("Return Type:\t " + operation.ReturnType + "\n");
WordApp.Selection.TypeText("Modifier:\t " + operation.Visibility + "\n");
WordApp.Selection.TypeText("Additional Properties:\t");
// Write some of the operation special modifiers such as static, abstract, and virtual
if (operation.IsStatic)
WordApp.Selection.TypeText("static ");
if (operation.IsVirtual)
WordApp.Selection.TypeText("virtual ");
if (operation.IsAbstract)
WordApp.Selection.TypeText("abstract ");
// Linebreak
WordApp.Selection.TypeParagraph();
// Loop through each operation parameter and type them all on a line in Microsoft Word
operation.Parameters.Restart();
WordApp.Selection.TypeText("Parameters:\t");
while (operation.Parameters.IsLast() == false)
{
With_Class.Parameter p = operation.Parameters.GetNext();
WordApp.Selection.TypeText(p.Name + ":" + p.Type + " ");
}
// Leave a space for the next operation
WordApp.Selection.TypeParagraph();
WordApp.Selection.TypeParagraph();
}

Conclusion

You may also note that you could probably extract most of this information directly from reflection, but  I suspect extracting object-oriented data using WithClass may be a bit less code and less complicated once you understand WithClass's COM structure. These same techniques for creating class reports using WithClass can also be extended to creating reports for State Charts and Interaction Diagrams.  

What's great about .NET and C#, is COM structures present in Applications such as Word, WithClass, or Excel, look and act like C# classes so you no longer have to distinguish them in your head as being "special" COM objects.  The fact that the COM architecture is hidden from you in this case is a good thing.