Valor Whisler
Senior Engineer, Crescent Division of Progress Software
March 17, 1997
Valor Whisler is a senior engineer for Crescent, a leader in the business of developing components and tools for Microsoft Visual Basic. He has a lead role in helping Crescent deliver high quality, innovative, and useful products. He developed the Repository Browser, which ships with Microsoft Visual Basic version 5.0.
Click to copy the files for the RepositoryTool sample for this technical article.
The Microsoft® Repository is a place to store and share objects between software tools, such as Microsoft Visual Basic® Add-Ins and custom reusable components. Microsoft Visual Basic version 5.0 is the first client for this repository—it ships with the repository engine, a tool information model (TIM) that describes a Visual Basic project, an add-in to populate the repository, and a browser to view repository data. Additionally, the Visual Modeler and the Visual Component Manager in the Microsoft Visual Basic, Enterprise Edition use the repository. It is likely that new versions of other Microsoft development tools will provide similar hooks to the repository. The repository was developed in order to deliver improved support in the following strategic areas:
At the core of the repository is the engine, a set of Component Object Model (COM) and Automation (formerly OLE Automation) interfaces that provide a way of reading and writing structured data to a relational database—specifically, a Microsoft Access or Microsoft SQL Server™ database. This engine exposes OLE objects with methods that are used to create and manage repositories, system descriptions (tool information models, or TIMs), and instances of system objects. You may have a tendency to think of this engine as the repository, but it may be more useful to think of the repository as a collective toolset: the engine, tool information models, repository tools, and repository data.
This paper will first discuss why the repository should be used and then delve deeper into conceptual aspects of the repository such as tool information and object models. It then provides some practical examples of using the repository by discussing the Repository Browser and Visual Component Manager. Finally, a fully functional set of code is provided to start programming the repository.
Although there is much to discuss about the repository, all of the reasons to use the repository can be distilled down to three important points. The repository provides:
The bottom line is this: the repository can enable tools that will provide completely new perspectives of the development process and also provide programmers with new development capabilities. To illustrate one new development perspective, take a look at the Visual Component Manager add-in that is available on the Microsoft Visual Studio™ 97 Web site for registered Microsoft Visual Basic owners (http://www.microsoft.com/vstudio/owner/default.asp). This product organizes and makes available components, tools, and projects that can be used in a Visual Basic project as well as many other features and functions. All of these components can be located, manipulated, and used from a single, centralized interface. The Visual Component Manager is discussed in more detail later in this document.
For more detailed information, see "What Is Microsoft Repository?" and "Repository Technology: A Key Enabler for Business Application Development."
Tool information models (TIMs) are descriptions of systems, or processes that are published in a repository. A repository can contain any number of TIMs, each describing a different system, or process. Each TIM defines the objects, interfaces, relationships, and properties that make up a system. You may also think of a TIM as a template for a particular type of system. Standardized TIMs allow for independent tools to interact with a common set of repository data. For example, the MdoTypeLib was created to describe Visual Basic projects. The add-in that ships with Microsoft Visual Basic 5.0 uses this model when creating instances of objects that describe actual projects. Likewise, any other toolmaker can use this model in the same way.
The model of a system can get quite complex and its representation in the browser may seem very different from the physical system it is designed to model. This is due to the fact that the repository requires you to think of objects in the context of how their interfaces relate to other objects, and furthermore that objects exist only in that they support one or more interfaces.This thought process can be difficult to master, but refreshingly useful when considering object interactions. To reduce the complexity of data displayed in the browser, it is possible to filter out the visualization of interfaces (on the View menu, click Filters). Figure 1 shows a typical Visual Basic project in the browser without the interfaces showing. Figure 2 in the next section shows the same project with the interfaces visible.
“Information Modeling Using Microsoft Repository,” a slide show with more information on TIMs, is available in the Technical Materials section of the Microsoft Repository Web site at http://www.microsoft.com/Repository/.
Figure 1. A Visual Basic project in the browser
The repository engine is a dynamic-link library (DLL) that provides the following:
Although these objects are accessible via COM and Automation interfaces, this paper will only be discussing the objects as they appear from within Visual Basic (Automation).
The numbers in Figure 2 describe the different types of repository objects as follows: (1) the repository has a set of interfaces it supports, such as IAnnotiationalProps; (2) one of its interfaces is IMpoProjectItemContainer, which describes a Visual Basic project and contains a (3) collection of relationships and properties (not visible in the figure); (4) a repository object, in this case a representation of a Visual Basic project, is visible. Notice that it also has interfaces, and so on.
Figure 2. Types of repository objects
When navigating through the objects in the repository, there is a repeating scheme: objects have interfaces, which have collections of relationships, which target other objects, which have interfaces, and so on. You will find this scheme provides a way to traverse the entire contents of a repository starting from the root object, or from any other repository object.
What is not clear from the browser's perspective is that repository data is not necessarily hierarchical in nature. It is possible for objects to have relationships to any other object. A repository is more of a three-dimensional or a web-data schema and cannot adequately be shown in the two-dimensional browser.
The repository browser that ships with Microsoft Visual Basic 5.0 provides a way to view the contents of any repository. As mentioned, the repository can contain any number of tool information models. Many tools will only use the TIMs in a particular repository that are related to their particular system, or process. The browser, on the other hand, can be considered a generic TIM and repository browser. That is, it will display information about all of the TIMs within any repository.
The browser employs a Microsoft Windows® Explorer–style interface with a tree view of data on the left and a detailed list view on the right. A toolbar provides access to browser functions.
Figure 3. The Microsoft Repository Browser
Relationships themselves are not visible in the browser, but are implied by the tree lines connecting the objects. The Relationship dialog box is used to view the relationship between the selected object and the source of its relationship. In addition, relationships can have properties, which can be viewed in the Properties tab of the Relationship dialog box. You can quickly navigate to the source object of a relationship by clicking the icon or text in the Source dialog box on the Relationship dialog box, or by pressing the Up to Relationship Source button on the toolbar.
Figure 4. The Relationship dialog box
The browser provides ways to filter the data visually and by the type of information that is shown. A filter dialog box (on the View menu, click Filters) facilitates the setting of the filters. Try changing the settings to see how the browser display is affected. Figure 5 shows the Type tab, which filters the type of data that is shown.
The Visual Component Manager is an add-in available to registered owners of the Professional or Enterprise versions of Visual Basic and can be downloaded from the Microsoft Visual Studio 97 Web site (http://www.microsoft.com/vstudio/owner/default.asp). It is a replacement for the Component Manager provided in Microsoft Visual Basicversion 4.0, but is better suited to enterprise-wide application development because it is based on the open and scalable repository. It facilitates code reuse and productivity through categorization, publishing, and retrieval of reusable components.
Figure 5. Visual Component Manager
From within the Visual Component Manager, a programmer can find and insert pieces of code, load a project template, launch a wizard, or add an ActiveX™ control to a project. It can also provide a common and centralized location for documentation such as programming standards, functional specifications, or architectural diagrams.
It is fairly easy to enumerate repository objects using the Automation interfaces. It is possible to find all objects in the repository that have interfaces to the root object by following the repeating pattern: objects have interfaces, which have collections of relationships, which target other objects, which have interfaces, and so on.
The following code segments add repository objects as nodes to a TreeView control. These code segments are extracted from the RepositoryTools sample that accompanies this article. This code performs basically the same functions as used by the repository browser, although it has been simplified for the sake of size by removing error checking and other code related to interface enhancement. To use it, open the sample Visual Basic 5.0 repository project. You will see in the code that executing this function requires nothing more than calling the EnumRepository function from a form and passing in a TreeView control as the argument.
'<Private>--------------------------------------------
Private BeenHere As Boolean
Private KeyCounter As Integer
Private ThisTree As TreeView
Private WorkingNode As Node
Private RepositoryRoot As RepositoryObject
Private ThisRepository As Repository
'</Private>-------------------------------------------
'-----------------------------------------------------
'<Purpose> Opens the repository and start its enumeration.
'-----------------------------------------------------
Public Sub EnumRepository(ThisTreeControl As TreeView)
'---- Cache the tree control.
Set ThisTree = ThisTreeControl
'------------------------------------------------
' Initialize repository object definition variables.
' This function can be found in 'RepEng.bas' which
' ships with the repository.
'------------------------------------------------
Call InitRepository
'------------------------------------------------
' Open the repository; when no arguments are included
' in the Open method, the default repository is opened.
'------------------------------------------------
Set RepositoryRoot = ThisRepository.Open()
'---- Add a node that represents the repository root.
Set WorkingNode = ThisTree.Nodes.Add(, , "Root", "Repository Root", 2)
'------------------------------------------------
' Enumerate the root interfaces. This will start
' a chain of enumerations of the entire repository:
' Interfaces --> enumerates object members
' Members --> enumerates relationships
' Relationships --> enumerates interfaces
'------------------------------------------------
Call EnumInterfaces(RepositoryRoot, "Root")
End Sub
'-----------------------------------------------------------------
'<Purpose> Enumerates all the interfaces to the RepNodes collection.
'-----------------------------------------------------------------
Private Sub EnumInterfaces(ThisObject As RepositoryObject, ParentKey As String)
Dim ObjectType As ClassDef
Dim ObjectInterface As Object
Dim TheseInterfaces As Object
Dim InstanceInterface As RepositoryObject
Dim TheseMembers As Object
Dim NodeKey As String
'---- Get the definitions of the implemented interface.
Set ObjectType = ThisRepository.object(ThisObject.Type)
Set TheseInterfaces = ObjectType.Interfaces
For Each InstanceInterface In TheseInterfaces
'---- Create a new key.
KeyCounter = KeyCounter + 1
NodeKey = "I" & KeyCounter
'---- Add the node to the TreeView.
Set WorkingNode = ThisTree.Nodes.Add(ParentKey, tvwChild, _
NodeKey, InstanceInterface.Name, 4)
'---- This example only loops once.
If (Not BeenHere) Then
'---- get the interface object
Set ObjectInterface = ThisObject(InstanceInterface.Name)
'---- Enumerate all of the members of the interface.
Set TheseMembers = InstanceInterface("IInterfaceDef").Members
Call EnumMembers(TheseMembers, ObjectInterface, NodeKey)
End If
Next
BeenHere = True
End Sub
Private Sub Class_Initialize()
Set ThisRepository = New Repository
End Sub
Private Sub Class_Terminate()
Set ThisRepository = Nothing
End Sub
'--------------------------------------------------------------------
'<Purpose> Enumerates all the members and adds object members to the RepNodes collection.
'<Note> A member is a collection, property, or method on an inteface.
'--------------------------------------------------------------------
Private Sub EnumMembers(TheseMembers As Object, ObjectInterface As Object, _
ParentKey As String)
Dim IsOrigin As Boolean
Dim NodeKey As String
Dim InstanceMember As RepositoryObject
Dim TheseRelationships As Object
'---- Enumerate the relationship collections on the interface.
For Each InstanceMember In TheseMembers
'---- Check if the ThisMember is a property def or collection def.
If (Not IsPropertyDef(InstanceMember.Type)) Then
'---- Create a new key.
KeyCounter = KeyCounter + 1
NodeKey = "M" & KeyCounter
'---- Add the node to the TreeView.
Set WorkingNode = ThisTree.Nodes.Add(ParentKey, tvwChild, _
NodeKey, InstanceMember.Name, 4)
'---- Test to see if it is an origin.
IsOrigin = (InstanceMember("ICollectionDef").IsOrigin <> 0)
'---- Don't show relationships back to origin.
If IsOrigin Then
'---- Create the relationships collection, and enumerate it.
Set TheseRelationships = _
ObjectInterface.Properties(InstanceMember.Name).Value
Call EnumRelationships(TheseRelationships, NodeKey)
End If
End If
Next
End Sub
'-----------------------------------------------------------------
'<Purpose> Enumerates all the objects attached to the
' relationships into the RepNodes collection.
'-----------------------------------------------------------------
Private Sub EnumRelationships(TheseRelationships As IRelationshipCol, _
ParentKey As String)
Dim NodeKey As String
Dim InstanceRelationship As Relationship
Dim TargetObject As RepositoryObject
'---- Enumerate the relationships in this collection for the object.
For Each InstanceRelationship In TheseRelationships
'---- cache the target object, this is what will be displayed
Set TargetObject = InstanceRelationship.Target
'---- add properties here
If (Not IsPropertyDef(InstanceRelationship.Type)) Then
'---- Create a new key.
KeyCounter = KeyCounter + 1
NodeKey = "R" & KeyCounter
'---- Add the node to the TreeView.
Set WorkingNode = ThisTree.Nodes.Add(ParentKey, tvwChild, _
NodeKey, TargetObject.Name, 4)
'----------------------------------------------
' Now enumerate the interfaces on the target of
' this relationship, starting the loop again.
'----------------------------------------------
Call EnumInterfaces(TargetObject, NodeKey)
End If
Next
End Sub
Within the various loops, objects are created and a representation of the object is displayed. For any given object there are various methods available that provide functions to delete or create an object, or to get and set property values.
For further reading and examples, there are additional documentation and code examples installed with the repository.
The Microsoft Repository is in its early stages of development, and thus its full strategic implications are yet to be discovered. It is a fascinating technology that has the potential to enhance the value of many software tools—for example, by solving the problems of dependency tracking and software reuse. Dependency tracking is a problem that will get even more daunting as the use of distributed, component-based computing expands. Regarding reuse, it can provide a central place for components, systems, and processes to exist. This common library may provide the platform to finally realize the reusability potentials that everyone has heard of, but few have been able to offer. Although its full potential is yet to be discovered, the repository, the associated tools such as the Visual Component Manager, and the object modeling tools such as Visual Modeler provide a substantial boost in productivity and manageability today.