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; }