Visual C++ Wrappers with Meta Data Services

Meta Data Services Programming

Meta Data Services Programming

Visual C++ Wrappers with Meta Data Services

The repository API 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 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 instructs 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 also 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:

  • A .tlh header file that contains definitions of all interfaces and identifiers.

  • A .tli header file that contains inline wrapper functions, which convert properties from their respective data types to the variant data type that the Invoke method expects. The .tli file is automatically included inside the .tlh file.
Generating the Wrappers

In order to make use of the dispatch support in Visual C++, add 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 instruct 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 support 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, you can use the mechanism mentioned previously to import any required type library. 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):

  • GetmemberName

  • PutmemberName

where memberName is replaced by the member name.

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

  • GetVisibility()

  • PutVisibility()
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 here:

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=1;n<pPackage->GetElements()->GetCount();n++)
{

pRO = pPackage->GetElements()->GetItem(n);   // Get the element # n 
// Use the element pRO 

See Also

COM Reference