There is a difference between storage and access. Storage focuses on the way a multidimensional array of items gets arranged in linear memory. Since all memory is linear memory, it is paramount to understand how arrays are arranged in linear memory. Access is the way a language allows interaction with a multidimensional array.

Since we are creating and reading arrays from a computer language, we must understand the language’s perspective on the array and how to access it.

Arrays in COM


In order to move an array around within the COM world, it must be described by a SAFEARRAY descriptor whose dimensions are defined by SAFEARRAYBOUND descriptors.

SAFEARRAY Descriptors

The SAFEARRAY descriptor has the following definition:

typedef struct SAFEARRAY
{
  USHORT cDims;
  USHORT fFeatures;
  ULONG cbElements;
  ULONG cLocks;
  PVOID pvData;
  SAFEARRAYBOUND rgsabound[ 1 ];
}
SAFEARRAY;

This structure describes different aspects of the safe array, such as total number of dimensions, cDims, flags indicating if the array is fixed and cannot be resized, fFeatures, if there are any locks on the array, cLocks, and then a pointer to the actual array data itself, pvData.

Usually, the SAFEARRAY descriptor is wrapped by the OLE Automation data type

Variant, and the Variant itself is passed around as the data type in method calls.

Either way, an array must be wrapped by a SAFEARRAY before it can be marshaled.

SAFEARRAYBOUND Descriptors

A SAFEARRAY can have an unlimited number of dimensions, whose dimension count is stored in cDims. For each dimension, there must an element of type SAFEARRAYBOUND, which stores the lower bound and number of elements in the dimension, as given by the structure:

typedef struct SAFEARRAYBOUND
{
  ULONG cElements; LONG lLbound;
}
SAFEARRAYBOUND;

The SAFEARRAY descriptor member rgsabound[] is an array of SAFEARRAYBOUND elements. (Visual Basic lets you define an element range such as “10 to 20” or “-10 to 10” such that the lLbound item on the dimension is not zero, but 10 and –10, respectively. For all of our examples, we assume the lower bound is zero.)

Note: In COM, items are frequently in reverse order than what you would expect, which is the case with the SAFEARRAY descriptor’s rgsabound[] member array.

You must specify the dimensions in reverse order. For example, if you are constructing an array of 3 rows by 5 columns (3x5), the first SAFEARRAYBOUND array item would have its cElements member set to 5, and the second item rgsabound[] array item would have its cElements member set to 3. However, you rarely set rgsabound[] yourself. The Win32 API calls to create safe arrays automatically set these values from information specified in the expected order (i.e., 3 and 5). Note that if you look in memory at the actual SAFEARRAY descriptor data, the rgsabound[] member array will be in reverse order.

Arrays in IDL


IDL arrays are stored in “scanline majority,” meaning that each scanline is contiguous in memory. Additionally, the dimensions are listed backwards from standard computer-science notation.

For example, to create an array of bytes with 5 columns and 3 rows, use the following code:

myarr = BYTARR(5, 3)

SAFEARRAYs and IDL arrays are arranged differently in linear memory; when you create an array in the COM world that you want to give to IDL, you must “convert the majority.” For how to do so in three programming languages, see 2D Array Examples.