Using the SDK from C++

This section provides a few simple examples that demonstrate how to use the StarTeam COM interfaces in C++.

Note that when using the COM interfaces from C++, you must include the following header file:

#include "StarTeamSDK.h

This header contains the declarations of the StarTeam COM interfaces. It should be located in the lib subfolder of the StarTeam SDK installation folder.

In addition, you will need to compile the file StarTeamSDK_i.c and link it into your project. This file defines the various class IDs (CLSIDs) and Interface IDs (IIDs) for the StarTeam COM APIs. This file is also located in the SDK lib subfolder.

Getting Started

The following example shows how to create a Server object, and establish a connection to a StarTeam Server, using the StarTeam COM interfaces in C++.

int main(int argc, char *argv[ ], char *envp[ ]) { 
 
    USES_CONVERSION; 
    CoInitialize(NULL); 
 
    const char* strAddress = "localhost"; 
    int nPort = 49201; 
    const char* strUser = "SampleUser"; 
    const char* strPassword = "MyPassword"; 
 
    HRESULT hr = S_OK; 
    IStServerFactory* Factory = NULL; 
    IStServer* Server = NULL; 
    try { 
        // Instantiate an StServerFactory object. 
        hr = CoCreateInstance( 
                        CLSID_StServerFactory, 
                        NULL, 
                        CLSCTX_SERVER,  
                        IID_IStServerFactory,  
                        (void**)&Factory); 
 
        // Use it to create an StServer object. 
        if (SUCCEEDED(hr)) { 
            hr = Factory->Create( 
                        T2OLE(strAddress),  
                        nPort,  
                        &Server); 
        } 
 
        // Establish a connection to the server. 
        // Optional - logOn() connects if necessary. 
        if (SUCCEEDED(hr)) { 
            hr = Server->connect(); 
        } 
 
        // LogOn as a specific user. 
        if (SUCCEEDED(hr)) { 
            long userID = -1; 
            hr = Server->logOn(
                      T2OLE(strUser), 
                      T2OLE(strPassword),  
                      &userID); 
        } 
 
        // Use the Server object to enumerate  
        // Projects, Views, etc. 
        // . . .  
 
        // Disconnect when finished. 
        if (SUCCEEDED(hr)) { 
            hr = Server->disconnect(); 
        } 
 
        // Clean up. 
        if (Server != NULL) { 
            Server->Release(); 
            Server = NULL; 
        } 
        if (Factory != NULL) { 
            Factory->Release(); 
            Factory = NULL; 
        } 
    }  
 
    // Error handling. 
    catch(...) { 
        if (Server != NULL) { 
            Server->Release(); 
        } 
        if (Factory != NULL) { 
            Factory->Release(); 
        } 
    } 
 
    CoUninitialize(); 
    return(0); 
} 

As you can see, using the StarTeam interfaces from C++ is much more complex than using them from other programming environments. This is not really due to any added complexity in the StarTeam APIs themselves, but is the nature of using COM from C++.

Describing COM functions such as CoInitialize(), CoCreateInstance(), and CoUninitialize() is beyond the scope of this document. Consult the reference documentation in the Win32 platform SDK.

The examples above use T2OLE() to enhance readability. T2OLE and USES_CONVERSION are macros, defined by the Microsoft Foundation Classes (MFC), that simplify the conversion of ASCII text to Unicode, which is required by COM. If you are not programming natively in Unicode, and you are not using MFC, then you can convert to Unicode using the Win32 APIs. For example:

int nLen = MultiByteToWideChar( 
                 CP_ACP, 0, strAddress, 
                 strlen(strAddress), NULL, 0); 
if (SysReAllocStringLen(&bstrAddress, NULL, nLen)) { 
    MultiByteToWideChar( 
                 CP_ACP, 0,  
                 strAddress, strlen(strAddress),  
                 bstrAddress, nLen); 
                 
    hr = Factory->Create(bstrAddress, nPort, &Server); 
 
    SysFreeString(bstrAddress); 
    bstrAddress = NULL; 
} 

When using the COM interfaces from C++, be sure that you use AddRef() and Release() to maintain the proper reference counts on the various StarTeam objects. The most common problems encountered while using the COM interfaces are a result of releasing an object prematurely or leaving an outstanding reference to an object when it is no longer needed.

You can simplify the management of object references in C++ by using smart pointers. Smart pointers are essentially C++ wrappers that maintain reference counts as the objects go in and out of scope. Microsoft Visual C++ supports an implementation of smart pointers that is easy to use. Consult your Visual C++ documentation for further details.

Collections and Enumeration

The following example shows how to enumerate the projects that are available on a given server, searching for the project with a given name.

IStProject* FindProject(IStServer* pServer, LPCTSTR szName) 
{ 
    HRESULT hr = S_OK; 
    IStProject* pProject = NULL; 
 
    IStCollection* pCollect = NULL; 
    LPDISPATCH pDispatch = NULL; 
    IEnumVARIANT* pEnum = NULL; 
    LPUNKNOWN pUnknown = NULL; 
    IStProject* pNext = NULL; 
    BSTR bstrName = NULL; 
 
    try { 
 
        // Get the value of the Projects property. 
        hr = pServer->get_Projects(&pCollect); 
 
        // The value is a StarTeam collection object, 
        // which supports the standard get__NewEnum() 
        // method of an automation collection. 
        if (SUCCEEDED(hr)) { 
            hr = pCollect->get__NewEnum(&pDispatch); 
            pCollect->Release(); 
            pCollect = NULL; 
        } 
 
        // Now that we have an automation collection, 
        // query for the IEnumVARIANT interface. 
        if (SUCCEEDED(hr)) { 
            hr = pDispatch->QueryInterface( 
                        IID_IEnumVARIANT,  
                        (void**)&pEnum); 
            pDispatch->Release(); 
            pDispatch = NULL; 
        } 
 
        // Use IEnumVARIANT to iterate over  
        // the members of the collection. 
        while (SUCCEEDED(hr) && (pProject == NULL)) 
        { 
            USES_CONVERSION; 
         
            // Retrieve the next element. 
            VARIANT v; 
            VariantInit(&v); 
            hr = pEnum->Next(1, &v, NULL); 
 
            // Are we at the end of the collection? 
            if (hr == S_FALSE) 
                break; 
 
            // Get the project object from  
            // the VARIANT record. 
            pUnknown = NULL; 
            hr = VariantChangeType(&v, &v, 0, VT_UNKNOWN); 
            if (SUCCEEDED(hr)) { 
                pUnknown = v.punkVal; 
            } 
            if (pUnknown == NULL) { 
                hr = E_INVALIDARG; 
            } 
 
            // Query for the IStProject interface. 
            if (SUCCEEDED(hr)) { 
                hr = pUnknown->QueryInterface( 
                            IID_IStProject,  
                            (void**)&pNext); 
            } 
            if (pUnknown != NULL) { 
                pUnknown->Release(); 
                pUnknown = NULL; 
            } 
 
            // Get the value of the Name property. 
            if (SUCCEEDED(hr)) { 
                hr = pNext->get_Name(&bstrName); 
            } 
 
            // See if the project name matches  
            // the one we're looking for. 
            if (SUCCEEDED(hr)) { 
                LPCTSTR szNext = OLE2T(bstrName); 
 
                SysFreeString(bstrName); 
                bstrName = NULL; 
 
                if (strcmp(szName, szNext) == 0) { 
                    // It's a match; save a reference to the 
                    // project object, since we'll be returning  
                    // it to the caller. 
                    pProject = pNext; 
                    pProject->AddRef(); 
                } 
            } 
 
            if (pNext != NULL) { 
                pNext->Release(); 
                pNext = NULL; 
            } 
        } 
 
        if (pEnum != NULL) { 
            pEnum->Release(); 
            pEnum = NULL; 
        } 
    } 
    catch(...) { 
        if (bstrName != NULL) { 
            SysFreeString(bstrName); 
        } 
        if (pUnknown != NULL) { 
            pUnknown->Release(); 
        } 
        if (pNext != NULL) { 
            pNext->Release(); 
        } 
        if (pEnum != NULL) { 
            pEnum->Release(); 
        } 
        if (pDispatch != NULL) { 
            pDispatch->Release(); 
        } 
        if (pCollect != NULL) { 
            pCollect->Release(); 
        } 
        throw; 
    } 
 
    return(pProject); 
} 

Once again, using the StarTeam interfaces from C++ is much more complex than using them from other programming environments. In Visual Basic, this same example required about a dozen lines of code!

Although the code required to manipulate a collection is extensive, the concepts involved are reasonably straightforward. Remember to keep the following points in mind:

  1. A collection in the StarTeam object model is implemented using IStCollection.
  2. IStCollection implements the standard get__NewEnum() method.
  3. get__NewEnum() returns an object that supports the standard IEnumVARIANT interface.
  4. Use the Next() method of the IEnumVARIANT interface to iterate over the members of the collection.
  5. The type of the VARIANT members of the collection depends on which collection is being examined. In most cases, the members will be IDispatch interfaces to some StarTeam-specific object. Use QueryInterface() to obtain the interface appropriate for the specific collection. For example, query for IStProject in a Projects collection, IStView in a View collection, and so on.

Handle reference counting with care. In particular, remember that when the Next() method of IEnumVARIANT returns a variant of type VT_DISPATCH or VT_UNKOWN, there is an outstanding reference to the enclosed interface pointer that must be released by the caller.

Error Handling

The StarTeam COM APIs support rich error messages through the use of the standard IErrorInfo interface. The following example demonstrates how to get a descriptive error message when one of the StarTeam APIs fails.

BSTR GetErrorMessage(HRESULT hr, REFIID riid, LPUNKNOWN p) 
{ 
    BSTR pErrorMessage = NULL; 
 
    // If the return code indicates an error ... 
    if (!SUCCEEDED(hr)) { 
 
        // If the object that generated the error supports 
        // the standard ISupportErrorInfo interface ... 
        ISupportErrorInfo* pSEI = NULL; 
        if (SUCCEEDED(p->QueryInterface( 
                        IID_ISupportErrorInfo,  
                        (void**)&pSEI))) { 
 
            // And supports error information on the  
            // interface that generated the error ... 
            if (pSEI->InterfaceSupportsErrorInfo(riid) == S_OK) { 
             
                // Get the IErrorInfo interface. 
                IErrorInfo* pEI = NULL; 
                if (GetErrorInfo(0, &pEI) == S_OK) { 
         
                    // Get the error message. 
                    pEI->GetDescription(&pErrorMessage); 
                    pEI->Release(); 
                } 
            } 
 
            pSEI->Release(); 
        } 
    } 
 
    return(pErrorMessage); 
} 
  
 

Back      Home