A simple C++ program

The simple C++ program shows how you can interoperate with BrainVoyager. The program accesses the BrainVoyager COM server to start it, to load a document and to invoke methods of the document object. It then goes beyond simple scripts by reading into memory a VMR file, inverting it's intensity values and then saving it under a new file name. It, thus, implements a simple new function which does not exist in BrainVoyager. Finally, BrainVoyager is called to load the new file for visualization.

Although it would be possible to access a loaded VMR file directly in BrainVoyager, the repeated reading and setting of intensity values via (out-of-process) COM access would take too much time. This simple program therefore shows how to mix calls to BrainVoyager with own program routines. See the chapter on "BrainVoyager file formats" for a description of all major file formats.

In the folder BrainVoyagerDemoClient, you find the source and executables for this simple demo program. You might want to use this project as the start for your own projects. If you work with Microsoft Visual C++ 5.0/6.0, you can use the provided project file to load and compile the demo program. The program is commented to explan the most important steps. To fully understand the code, you should consult a book on COM programming.

The way how BrainVoyager is accessed in the present example should work with any C++ compiler under Windows operating systems. If you are using Microsoft Visual C++, there is a much simpler way to access COM servers. This other method is highly recommend for its simplicity and is shown in the "BrainVoyagerEasyClient" program.

The code below also shows how to execute BrainVoyager on a remote computer from C++. To run this code, you must replace "REMOTEPC" with the actual name of your target computer.


// BrainVoyagerDemoClient.cpp
//
	  #define _WIN32_DCOM	   // needed for CoCreateInstanceEx in Win95/98
#include <objbase.h>          // for CoCreateInstanceEx
#include <fstream.h>
#include <comdef.h>	          // for _bstr_t data type (Microsoft specific)
#include "BrainVoyagerIDL.h"  // includes definition of BrainVoyager's COM interfaces
// Forward declarations
BOOL MyLoadVMR(const char *filename);
BOOL MySaveVMR(const char *filename);
void ConvertCharToBSTR(char *char_string, BSTR *bstr_string);
BYTE *Vol;             // Pointer to VMR data set, global in this example
int DimX, DimY, DimZ;  // Dimensions of loaded VMR data set, global in this example
IApplication *pApplication = NULL;	  // Variable for the basic IApplication interface
IDocument *pDoc  = NULL;              // Variables for interfaces to two documents
IDocument *pDoc2 = NULL;
void main()
{
	CoInitialize(NULL);  // Initialize COM
	IUnknown *pUnknown = NULL;
	BOOL RemoteServer = FALSE;
	HRESULT hr;
	if(RemoteServer)
	{
		WCHAR *RemoteComputer = L"REMOTEPC";
		COSERVERINFO si;
		si.pwszName = RemoteComputer;
		si.dwReserved1 = 0;
		si.pAuthInfo = NULL;
		si.dwReserved2 = 0;
		MULTI_QI mqi[1];
		mqi[0].pIID = &IID_IUnknown;
		mqi[0].pItf = NULL;
		mqi[0].hr = 0;
		hr = CoCreateInstanceEx(CLSID_BrainVoyager, NULL, CLSCTX_REMOTE_SERVER, &si, 1, mqi);
		if(hr == S_OK)
			pUnknown = mqi[0].pItf;
	}
	else
	{
		// Create instance of BrainVoyager and retrieve basic IUnknown interface
		hr = CoCreateInstance(CLSID_BrainVoyager, NULL, CLSCTX_SERVER, IID_IUnknown, reinterpret_cast<void**>(&pUnknown));
		if(!SUCCEEDED(hr))
		{
			MessageBox(NULL, "ERROR: Unable to invoke BrainVoyager via COM !", "BrainVoyager demo client", MB_OK);
			return;
		}
	}
	// BrainVoyager is now running, now get the IApplication interface via the IUnknown interface
	hr = pUnknown->QueryInterface(IID_IApplication, reinterpret_cast<void**>(&pApplication));
	
	pUnknown->Release(); // IUnknown no longer needed
	if(!SUCCEEDED(hr))
		return;
	struct IDispatch *pDocDisp;  // Get IDispatch interface first, then get the IDocument interface
	
	BSTR pathname;
	char path[_MAX_PATH], filename[_MAX_PATH];
	// Adjust the following path if necessary !!
	strcpy(path, "C:\\cg2_recons\\");
	strcpy(filename, path);
	strcat(filename, "cg2_tal.vmr");
	ConvertCharToBSTR(filename, &pathname);	// We have to convert C strings to Automation BSTR strings
	// Use the IApplication interface to call its OpenDocument method
	hr = pApplication->OpenDocument(pathname, &pDocDisp);
	
	SysFreeString(pathname);  // free resources
	if(!SUCCEEDED(hr))
		return;
	// Get IDocument from the IDispatch interface, then we can call document methods
	hr = pDocDisp->QueryInterface(IID_IDocument, reinterpret_cast<void**>(&pDoc));
	if(!SUCCEEDED(hr))
		return;
	pDocDisp->Release();
	// Call document methods
	//pDoc->SetZoomLevel(2);
	pDoc->Hide3DVolumeTools();
	// Now let's work on a BrainVoyager file format: a VMR file
	int new_val;
	if(MyLoadVMR(filename))  // load the VMR file into memory, for details see function definition below
	{
		// Loop through all voxels and invert intensity
		int x, y, z;
		for(x=0; x<DimX; x++)
		{
			for(y=0; y<DimY; y++)
			{
				for(z=0; z<DimZ; z++)
				{
					new_val = Vol[z*DimY*DimX + y*DimX + x];
					if((new_val > 30) && (new_val < 226))  // > 10
						new_val = 225 - new_val + 10;
					Vol[z*DimY*DimX + y*DimX + x] = (BYTE)max(0, new_val);
				}
			}
		}
		// Now we save the inverted data set under a new file name:
		strcpy(filename, path);
		strcat(filename, "cg2_tal_inv.vmr");
		MySaveVMR(filename); // for details, see function definition below
		free(Vol);  // free memory
		// Now we tell BrainVoyager to show the new file
		_bstr_t pathname2 = filename; // Simpler handling of C to BSTR conversion, but Microsoft specific!
		//ConvertCharToBSTR(filename, &pathname);
		hr = pApplication->OpenDocument(pathname2, &pDocDisp);
		//SysFreeString(pathname);
		// Finally, we call methods for the new document (as shown above)
		hr = pDocDisp->QueryInterface(IID_IDocument, reinterpret_cast<void**>(&pDoc2));
		if(!SUCCEEDED(hr))
			return;
		pDocDisp->Release(); // no longer needed
		// pDoc2->SetZoomLevel(2);
		pDoc2->Hide3DVolumeTools();
		// If you want to clean up:
		// Sleep(2000);
		// pDoc2->Close();
		pDoc2->Release();
	}
	
	// If you want to clean up:
	// Sleep(2000);
	// pDoc->Close();
	pDoc->Release();
	// If you want to clean up:
	// Sleep(1000);
	// pApplication->Exit();
	pApplication->Release();
	CoUninitialize(); // Uninitialize COM
}
// Helper function to convert a C string to a COM automation BSTR string
//
void ConvertCharToBSTR(char *char_string, BSTR *bstr_string)
{
	WCHAR wchar_string[256];
	for(int i=0; i<(int)strlen(char_string); i++)
	{
		wchar_string[i] = char_string[i];
	}
	wchar_string[i] = '\0';
	*bstr_string = SysAllocString(wchar_string);
}
// Accessing a BrainVoyager file format: the VMR file
//
BOOL MyLoadVMR(const char *filename)
{
	ifstream VMRFile( filename, ios::binary | ios::nocreate );
	if( !VMRFile ) 
	{
		cout << "ERROR: Cannot open VMR file." << endl;
		return FALSE;
	}
	unsigned short Nx, Ny, Nz;
	
	VMRFile.read((char *)(&Nx), sizeof(unsigned short));
	VMRFile.read((char *)(&Ny), sizeof(unsigned short));
	VMRFile.read((char *)(&Nz), sizeof(unsigned short));
	DimX = (int)Nx;
	DimY = (int)Ny;
	DimZ = (int)Nz;
	cout << Nx << " " << Ny << " " << Nz << endl;
	// Allocate memory for a data block with the dimensions read from the file  if((Vol = (BYTE *)malloc(Nz*Ny*Nx*sizeof(BYTE))) == NULL)
	{
		cout << "ERROR: Cannot allocate memory for VMR file." << endl;
		return FALSE;
	}
	// Now read the data into memory
	VMRFile.read(Vol, Nx*Ny*Nz*sizeof(BYTE));
	return TRUE;
}
BOOL MySaveVMR(const char *filename)
{
	ofstream VMRFile( filename, ios::binary | ios::trunc );
	if( !VMRFile ) 
	{
		cout << "ERROR: Cannot open VMR file." << endl;
		return FALSE;
	}
	unsigned short Nx, Ny, Nz;
	Nx = (unsigned short)DimX;
	Ny = (unsigned short)DimY;
	Nz = (unsigned short)DimZ;
	cout << Nx << " " << Ny << " " << Nz << endl;
	
	VMRFile.write((char *)(&Nx), sizeof(unsigned short));
	VMRFile.write((char *)(&Ny), sizeof(unsigned short));
	VMRFile.write((char *)(&Nz), sizeof(unsigned short));
	VMRFile.write(Vol, Nx*Ny*Nz*sizeof(BYTE));
	return TRUE;
}