Visual C++ Dispatch Wrappers with Microsoft Repository

Overview

Microsoft® Repository is based on dispatch interfaces. This means that all properties are manipulated through the Invoke method that the IDdispatch interface exposes. Using dispatch interfaces from programming languages that are v-table based, such as Microsoft Visual C++®, can be rather cumbersome. 

Visual C++ version 6.0 provides support for using dispatch interfaces in an easier way than before. It does this through the #import directive. The #import directive tells the Visual C++ compiler to read the type library given as a parameter to the directive, and to create v-table based wrappers for the type library. The compiler does this on the fly, and it even updates the wrappers if the type library is updated.

The compiler generates the following two header files with the same name as the type library:

Generating the wrappers

In order to make use of the dispatch support in Visual C++, put the following statement at the top of one of the .cpp files:

#include <atlbase.h>            // Required for smart pointer support
#import “rtim.tlb” named_guids
// The following using-directive allows other type libraries to 
// reference Repository engine objects:
using namespace RepositoryTypeLib;   
#import “uml.tlb”  named_guids
using namespace UML;

The atlbase.h header file is required to support smart pointers. The next two lines tell the compiler to generate wrapper classes for the main interfaces defined by the Repository engine. The compiler automatically wraps type libraries into namespaces that have the same name as the type library. This is done to limit the possibility of name clashes between type libraries. Unfortunately, the wrapper generator does not know how to handle references between type libraries. Therefore, the using namespace directive is required to automatically map the Repository engine interfaces into the default namespace.

After the compiler generates wrappers for the Repository engine interfaces, any required type library can be imported by using the same mechanism as indicated above. Make sure that the type libraries are imported in a correct dependency order.

When the wrapper is generated, the compiler creates the following two functions for each interface member (such as property or collection):

where member-name is replaced by the member name.

For example, the Visibility method on the IUMLModelElement interface (IUMLModelElement.Visibility) will be wrapped into the following methods:

Using the wrappers

After the compiler generates wrappers for dispatch based interfaces, smart pointer templates can be used to manipulate these objects. To define a smart pointer for an interface, use a declaration similar to the following:

CComPtr<IRepository> pRep;

This defines a smart pointer for the IRepository interface. To instantiate a repository and assign it to the smart pointer, use the CoCreateInstance method of the smart pointer, as shown below:

hr = pRep.CoCreateInstance(CLSID_Repository,NULL);

After instantiating the repository, it is possible to use methods defined on the IRepository interface to open a repository database as follows:

CComPtr<IRepositoryObject> pRootRO;
pRootRO = pRep->Open(“C:\\test.mdb”,””,””,0);

The methods defined on the dispatch interface are accessed using the -> operator, while helper functions such as CoCreateInstance are accessed using the dot (.) operator.

After opening a Repository database, it is possible to use the wrappers and the smart pointers to access any object in the repository. For example:

CComPtr<IUmlPackage> pPackage;
CComPtr<IRepositoryObject> pRO;
hr = pRootRO.QueryInteface(&pPackage);
for (long n=0;n<pPackage->GetElements()->GetCount();n++)
{
   pRO = pPackage->GetElements()->GetItem(n);   // Get the element # n 
   // Use the element pRO 
   …
   …
}

 

(c) 1988-1998 Microsoft Corporation. All Rights Reserved.