SAFEARRAYS are a Microsoft device to help control multi-dimensional arrays, and avoid memory leaks. Each dimension of the array may have a differing number of elements. SAFEARRAYS and associated functions are documented by Microsoft. Please find more on MSDN website.
SAFEARRAYS are returned/passed in as parameters to some of the methods in the Inventor API, and thus may be unavoidable for some applications.
In order to help illustrate the usage of SAFEARRAYS as parameters to the API methods let us consider the example of getting the facet information from the surface body of a part model.
The SurfaceBody->GetFacets() method returns the facet information as SAFEARRAYS. Before the actual facet information can be accessed, the tolerance values must be obtained. This is also returned in a SAFEARRAY.
The following code section shows how to declare and use SAFEARRAYS. The acceptable tolerance values for the facets are returned in the ExistTols SAFEARRAY. We could choose any element from the one-dimensional array as the desired tolerance value for the facets. The facet information is also returned as SAFEARRAYS.
The contents of the arrays can be accessed using the SafeArrayAccessData method(remember to do SafeArrayUnaccessData to unlock later). This is shown in Method1 of the code segment. But, when we use SafeArrayAccessData, it does not tell us how the array is organized (dimensions, elements etc..) but, just returns the contents. In our case, the arrays have just one dimension. For example, the vertex coordinates has the following structure:
X1,Y1,Z1, X2,Y2,Z2,X3,Y3,Z3 …….. Xn,Yn,Zn where n is the no. of vertices (Vertices)
With this structure known, you could access the contents of the array and store them in a double array. This is shown in Method2.
Finally, remember to deallocate memory and destroy the SAFEARRAYS.
///////////////////////////////////////////////////////////////////////////////
// Use: SAFEARRAY demo
//
///////////////////////////////////////////////////////////////////////////////
void SafeArrayDemo(
CComPtr<Application> pInventorApp,
CComPtr<PartDocument> pPartDoc)
{
HRESULT hr;
CComPtr<PartComponentDefinition> pPartDef;
hr = pPartDoc->get_ComponentDefinition(&pPartDef);
CComPtr<SurfaceBodies> pSurfBodies;
hr = pPartDef->get_SurfaceBodies(&pSurfBodies);
long index = 1;
CComPtr<SurfaceBody> pSurfBody;
hr = pSurfBodies->get_Item(index,&pSurfBody);
// Use raw SAFEARRAYS
// get the existing tolerance values
// declare a SAFEARRAY (has no contents)
SAFEARRAY *ExistTols = NULL;
//get the existing tolerance values
long TolCount;
hr = pSurfBody->GetExistingFacetTolerances(
&TolCount,
&ExistTols);
// Get the first tolerance value,
// you can choose from existing values
// if there is more than one
double TolIndex;
long nTolCount=0;
hr = SafeArrayGetElement(
ExistTols,
&nTolCount,
(void*)&TolIndex);
// Now get the facets information
// no. of vertices and facets
long Vertices, Facets;
SAFEARRAY *VertexCoords = NULL;
SAFEARRAY *NormalVects = NULL;
SAFEARRAY *VertexIndices = NULL;
// Call the method to get facet information
hr = pSurfBody->GetExistingFacets(
TolIndex,
&Vertices,
&Facets,
&VertexCoords,
&NormalVects,
&VertexIndices);
// From the SAFEARRAY get the contents
// Method1 (SafeArrayAccessData)
// declare an array of doubles
// to store vertex coordinates
double* vertexcoords1;
hr = SafeArrayAccessData(
VertexCoords,
(void**)& vertexcoords1);
// remember to do SafeArrayUnaccessData
// after accessing the SAFEARRAY contents
hr = SafeArrayUnaccessData(VertexCoords);
// Method2 (SafeArrayGetElement)
double *vertexcoords2;
vertexcoords2 = new double[Vertices*3];
for(long i=0; i<Vertices; i++)
{
long x_index = i*3;
double x;
SafeArrayGetElement(
VertexCoords, &x_index, (void*)&x);
long y_index = i*3 + 1;
double y;
SafeArrayGetElement(
VertexCoords, &y_index, (void*)&y);
long z_index = i*3 + 2;
double z;
SafeArrayGetElement(
VertexCoords, &z_index, (void*)&z);
vertexcoords2[x_index] = x;
vertexcoords2[y_index] = y;
vertexcoords2[z_index] = z;
}
//remember to deallocate SAFERRAYS
SafeArrayDestroy(ExistTols);
SafeArrayDestroy(VertexCoords);
SafeArrayDestroy(NormalVects);
SafeArrayDestroy(VertexIndices);
delete[] vertexcoords2;
}
Apart from the method of dealing with the raw SAFEARRAYS, you could also use the CSafeArray class declared in the Inventor SDK (<SDK_PATH>\Include\SafeArrayUtil.h). This class is very useful because it simplifies the use of SAFEARRAYS by wrapping the contents of the array and providing methods that allow easy access to the contents of the array.
The CSafeArray also provides methods that allow you to easily initialize and copy SAFEARRAYs. In essence, it stores the SAFEARRAY as a private data member and provides useful wrapper methods, these wrapper methods internally deal with the actual creation/manipulation of the contents of the SAFEARRAY. Thus, by using the methods of the class, the cumbersome work of having to deal with SAFEARRAYs is delegated to these wrapper methods.
///////////////////////////////////////////////////////////////////////////////
// Use: CSafeArray demo (SafeArrayUtil.h)
//
///////////////////////////////////////////////////////////////////////////////
void CSafeArrayDemo(
CComPtr<Application> pInventorApp,
CComPtr<PartDocument> pPartDoc)
{
HRESULT hr;
CComPtr<PartComponentDefinition> pPartDef;
hr = pPartDoc->get_ComponentDefinition(&pPartDef);
CComPtr<SurfaceBodies> pSurfBodies;
hr = pPartDef->get_SurfaceBodies(&pSurfBodies);
long index = 1;
CComPtr<SurfaceBody> pSurfBody;
hr = pSurfBodies->get_Item(index,&pSurfBody);
long TolCount;
CSafeArrayDouble ExistTols;
hr = pSurfBody->GetExistingFacetTolerances(
&TolCount,&ExistTols);
double TolIndex;
long nTolCount=0;
TolIndex = ExistTols.operator [](nTolCount);
long Vertices, Facets;
CSafeArrayDouble VertexCoords;
CSafeArrayDouble NormalVects;
CSafeArrayDouble VertexIndices;
hr = pSurfBody->GetExistingFacets(
TolIndex,
&Vertices,
&Facets,
&VertexCoords,
&NormalVects,
&VertexIndices);
// From the SAFEARRAY get the contents
// METHOD1 (equivalent of SafeArrayAccessData)
// declare an array of doubles
// to store vertex coordinates
double *vertexcoords1;
vertexcoords1 = VertexCoords.DataBase();
//the above calls SafeArrayAccessData internally
// METHOD2 (equivalent of SafeArrayGetElement)
double* vertexcoords2;
vertexcoords2 = new double[Vertices*3];
for(long i=0; i<Vertices; i++)
{
long x_index = i*3;
double x;
x = VertexCoords.operator [](x_index);
long y_index = i*3 + 1;
double y;
y = VertexCoords.operator [](y_index);
long z_index = i*3 + 2;
double z;
z = VertexCoords.operator [](z_index);
vertexcoords2[x_index] = x;
vertexcoords2[y_index] = y;
vertexcoords2[z_index] = z;
}
// No need to destroy CSafeArray's
// the destructor destroys the internal SAFEARRAY
//delete double arrays
delete[] vertexcoords2;
}