C++ and the API

I am not sure how many people out there would even dream of using C++ together with the API but considering there really is no documentation out there for this type of project I thought that I would try to put together some notes on how it is possible. The API is COM based and there is no reason why you cannot use C++ (just possibly more of a question of why you would ever want to). This posting assumes a basic knowledge of C++.

Firstly why would you every want to use C++. Sometimes this is a necessity. You may be using old C or C++ legacy code that you want to integrate directly with the API. In which case here are a few tips to get you started.

Firstly it is not so simple to just add a reference to the code. Instead you need to find the .tlb file and reference that instead. It should be that simple but in order for it to work I also had to rename some of the tokens used in the COM object so that they would work with C++. This is quite self explanatory in the code below that was added to the project’s stdafx.h header file.

#import "C:\Program Files\Blackbaud\The Raisers Edge 7\tlb\bbreapi7.tlb" auto_search rename("EOF","EndOfFile") rename("GetUserName","BBGetUserName")

#include "stdafx.h"#include "RE7App.h" // The one and only application object   

CWinApp theApp;   

using namespace std;   

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])   

{    

 int nRetCode = 0;   

 CString sOutput;   

// initialize MFC and print and error on failure    

 if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))   

 {    

  _tprintf(_T("Fatal Error: MFC initialization failedn"));   

  nRetCode = 1;   

 }    

 else    

 {    

  _tprintf(_T("Start:" + CTime::GetCurrentTime().Format(L"%Y-%m-%d %H:%M:%S") + "n"));   

//Initialize the COM environment    

  CoInitialize ( NULL );

So far pretty boring housekeeping. Now is where the specific RE code comes into play. First we create the REAPI object. If you look at the tlb file (in an object viewer) you will see that for many objects there is a class implementation, an interface and a pointer to the interface. Unlike VB where this is all invisible in C++ you have to manage these objects. You have to work with the interface pointers which in turn interact with the implementation classes. You create them using the id of the actual class as shown below.

BBREAPI7::IBBSessionContext* m_pSessionContext;   

         BBREAPI7::_REAPIPtr m_RE7Ptr; HRESULT hr;   

  hr = m_RE7Ptr.CreateInstance(__uuidof(BBREAPI7::REAPI));   

//initialize with amServermode to avoid   

 //splash screen and login prompt. If there is an error logging in then an exception is thrown.   

  VARIANT_BOOL vbRet; 
  CString serialNumber = L"WRE11111"; 
  CString userName = L"Supervisor"; 
  CString password = L"Admin"; 
  int dbNumber = 50;   

try {    

  vbRet = m_RE7Ptr->Init ((_bstr_t)serialNumber, (_bstr_t)userName, (_bstr_t)password, dbNumber, "", BBREAPI7::amServer );   

    	}    

  catch(_com_error comError){   

 	   return FALSE;    

    	}    

m_pSessionContext = m_RE7Ptr->SessionContext;   

//Now we create a collection of records. We have to create an IBBUICollection object as well since that takes care of accessing the fields.   

  BBREAPI7::_CRecordsPtr pRecords;   

  BBREAPI7::IBBUICollection* m_pCollection;   

hr = pRecords.CreateInstance(__uuidof(BBREAPI7::CRecords));   

//This is how we have to convert between the two objects    

  hr = pRecords.QueryInterface(   

  __uuidof(BBREAPI7::IBBUICollection),   

  (void**) &m_pCollection);   

pRecords->Init(&m_pSessionContext,BBREAPI7::tvf_record_Constituents,"",TRUE);   

//The collection only returns an IBBDataObject so we create both this and a CRecord   

  BBREAPI7::_CRecordPtr recordPtr; 
  BBREAPI7::IBBDataObjectPtr pDataObjPtr;   

hr = recordPtr.CreateInstance(__uuidof(BBREAPI7::CRecord));   

//We get object 1 from the collection (you have to remember which collections are zero based and which are based from 1!    

  pDataObjPtr = m_pCollection->Item[1];   

//Now convert to the record pointer to work with    

  hr = pDataObjPtr.QueryInterface(__uuidof(BBREAPI7::_CRecord), 
  (void**) &recordPtr);   

CString sLastName = CString(recordPtr->Fields[BBREAPI7::RECORDS_fld_LAST_NAME].bstrVal); 
  MessageBox(NULL, sLastName , L"RE7App", MB_SYSTEMMODAL|MB_ICONINFORMATION);   

//Now close down objects. 
  recordPtr->CloseDown(); 
  pRecords->CloseDown();   

  BBREAPI7::IBBDisposable* pDisposable;   

try 
 	{ 
 //We have to close down the m_RE7Ptr object. This is done through an interface and not directly    

  hr = m_RE7Ptr.QueryInterface( 
  __uuidof(BBREAPI7::IBBDisposable), 
  (void**) &pDisposable);   

hr = pDisposable->CloseDown();   

 	}    

  catch(_com_error comError) 
 	{    

 	return FALSE; 
 	}    

//Unintialize COM environment   

  CoUninitialize();

This is quite complicated code if you are not used to working with it (indeed I have put it together from a much larger collection of classes apologies for any errors). If you have any questions or get stuck then let me know and I will try to help you out.

One thought on “C++ and the API

  1. Pingback: VBA/API and PHP - Page 2 - Blackbaud User Society - Forum

Comments are closed.