With the release of Microsoft Visual C++ version 6 there is now "built-in" COM support. A template class called _com_ptr_t encapsulates an interface pointer and silently handles calls to QueryInterface, AddRef, and Release. These smart pointers can be created with a Microsoft provided macro, _COM_SMARTPTR_TYPEDEF, creating classes named after the interface with the suffix Ptr. In addition, Visual C++ can wrap COM interfaces with classes that provide a number of features that enhance the readability and use of the underlying COM classes. They simulate properties, provide bracket operators for Item methods, return arguments marked as retval in the type library as actual return values, and change error return codes into C++ exceptions. You can easily generate these wrapper classes for an application by importing a type library. For the PI-SDK this looks like
This statement generates files during the build, which contain the wrapper class implementations encapsulated with a smart pointer template. This is discussed in more detail in the C++ code tutorial in this document.
In addition to making it easier to instantiate and call COM interface methods and properties, the wrapper classes handle COM errors differently. (Note this type of error handling is also used in the _bstr_t and _variant_t classes). These classes call a routine called _com_raise_error, which takes an HRESULT and an IErrorInfo pointer, and constructs a type called _com_error that is then thrown as an exception. The _com_error type is defined in the file
comdef.h. The class definition shows accessor functions for the encapsulated information, in particular:
Error() returns the HRESULT
Description() returns the descriptive string
You can also retrieve the other portions of the IErrorInfo interface if they have been set, including a help context, a help file, and a source string.
This in effect turns a calling convention, which always specifies errors in the return value, to one that uses C++ exception handling. The following bit of code assumes that the #import has been performed, making the xxxPtr classes available.
void GetTagDescriptor(_bstr_t bServer, _bstr_t bTagName,
_variant_t & vDesc)
// navigate through the PISDK
ServersPtr pServs = pSDK->GetServers();
ServerPtr pServ = pServs->GetItem(bServer);
PIPointsPtr pPts = pServ->GetPIPoints();
PIPointPtr pPt = pPts->GetItem(bTagName);
PointAttributesPtr pAtts = pPt->GetPointAttributes();
PointAttributePtr pDesc =
vDesc = pDesc->GetValue();
// Check for errors thrown by smart pointers
tprintf(_T("Error %x occurred. Message was %s\n"),
e.Error(), (char *)e.Description());
// Check for non COM errors being thrown
In the code above we create a smart pointer to the PISDK interface wrapper using a constructor of the smart pointer class. We then declare a smart pointer to a Servers interface wrapper and assign it the interface returned from the PI-SDK Server property. Similar calls are made to walk down the hierarchy and discover the PIPoint's descriptor. If any failures occur, execution is transferred to the catch statement and the error is handled. If it was desirable to propagate the error upwards it could be thrown again from within the catch. The catch with the ellipsis is provided to catch non- COM exceptions that might be thrown by the operating system.