In these examples, you will create a 2D array that has 3 rows by 5 columns (3x5). Since the ultimate goal is to give the array to IDL for processing, pretend it is an “image.” You will set the first row to all red, the second row to all green, and the third row to all blue. The conceptual layout of the array is as follows:

rrrrr
ggggg
bbbbb

We will see shortly that even though the conceptual 2D layout is the above, the actual layout in linear memory is quite different between SAFEARRAYs and IDL.

In the examples below, the “red” value is really the ASCII character ‘r’, “green” is the ASCII character ‘g’, and so on. Use this scheme so that when you look at the actual memory, you will see the letters “rgb”, which makes it easy to read. It is much less confusing than using the cardinal numbers 1, 2, 3, when you are also talking about ordinal numbering involving 1, 2, 3.

These examples illustrate how different languages store data. You do not need to include such code in your applications to make them work; the wrapper does the conversion for you.

Visual Basic


Here is how to create the RGB array (matrix) in Visual Basic. This example, by default, creates a valid SAFEARRAY that is compliant with the information above, and stored within a Variant when passed as a parameter in a method call (not shown).

Const RED As Byte = 114
Const GREEN As Byte = 103
Const BLUE As Byte = 98
‘ This creates an array with dimension indices 0..2 & 0..4
‘ inclusive:
‘ i.e., it creates a 3x5 array; with “lower bounds” set to 0.
Dim m(2, 4) As Byte
For I = 0 To 4
m(0, I) = RED
m(1, I) = GREEN
m(2, I) = BLUE
Next I

Resulting linear memory:

rgbrgbrgbrgbrgb

Resulting SAFEARRAY.rgsabounds:

[0,5], [0,3]

Note the reversed order!

C++ Using ATL SAFEARRAY Wrapper Objects


This example uses the ATL Safearray wrapper objects: CComSafeArrayBound and CComSafeArray, which wraps the calls to the native Win32 Safearray API calls.

CComSafeArrayBound bound[2];
bound[0].SetCount(3); // 3 rows
bound[1].SetCount(5); // 5 columns
CComSafeArray<byte> matx(bound,2);
long ndx[2];
for ( int i = 0; i < 5; i++ )
{
  ndx[0] = 0;
  ndx[1] = i;
  matx.MultiDimSetAt(ndx,'r');
  ndx[0] = 1;
  ndx[1] = i;
  matx.MultiDimSetAt(ndx,'g');
  ndx[0] = 2; ndx[1] = i;
  matx.MultiDimSetAt(ndx,'b');
}

Resulting linear memory:

rgbrgbrgbrgbrgb

Resulting SAFEARRAY.rgsabounds:

[0,5], [0,3]

Observe that when the CComSafeArrayBound array is created, it is initialized in the conceptually correct order (i.e., specifying the “3 rows” by “5 columns”). But, if you look at the actual SAFEARRAY.rgsabounds[] element in memory, you see that they were reversed when the array was created.

C++ Using SAFEARRAY API Calls and Creating Different Memory Layout


C++ has the flexibility to create SAFEARRAYs in many different ways. By calling the SAFEARRAY API calls directly and judiciously, you can create a SAFEARRAY with data in a different order than what is normally expected. IDL and traditional SAFEARRAY data ordering are different. This example puts the data into the SAFEARRAY in the same order as IDL expects it. In other words, it puts the data in the opposite order that is used for SAFEARRAYs when you use the API calls to set individual data elements.

First, step back and see how the C++ language stores multidimensional arrays. If you have the following declaration:

byte data[3][5] = {
'r','r','r','r','r',
'g','g','g','g','g',
'b','b','b','b','b' };

the resulting linear memory looks like this:

rrrrrgggggbbbbb

This is the same order that IDL expects. However, C++ accesses the memory in the opposite way that IDL would access the same data. For example, to set the kth element of the first row (0-indexed), here is how the two languages compare:

C++:

data[0][k] = value;

IDL:

data[k,0] = value

However, the resulting linear memory layout is the same.

This example creates the 2D RGB array in C++ using the SAFEARRAY API calls and arranging memory in the same layout as IDL.

// First, create the linear memory in the format: rrrrrgggggbbbbb
byte data[3][5];
for ( int i = 0; i < 5; i++ )
{
  data[0][i] = 'r';
  data[1][i] = 'g';
  data[2][i] = 'b';
}
SAFEARRAYBOUND sab[2];
sab[0].lLbound = 0;
sab[0].cElements = 3; // 3 rows
sab[1].lLbound = 0;
sab[1].cElements = 5; // 5 columns
SAFEARRAY* psa = SafeArrayCreateEx(VT_UI1, 2, sab, NULL);
 
// By copying the source data into the safearray data area,
// we can create the data in a different order. Since the
// source data is in the same order as IDL expects, this creates
// a SAFEARRAY with a non-standard ordering.
memcpy(psa->pvData, data, sizeof(data));

Resulting linear memory:

rrrrrgggggbbbbb

Resulting SAFEARRAY.rgsabounds:

[0,5], [0,3]

The consumer of this array needs some indication that the order is different than standard SAFEARRAYs and that it would not need to be converted before passing off to IDL.

Here is how to create the 2D RGB array in IDL pro code:

arr = BYTARR(5, 3)
for i=0,4 do begin
  arr[i,0] = 114B
  arr[i,1] = 103B
  arr[i,2] = 98B
endfor

Resulting linear memory:

rrrrrgggggbbbbb

Calling help, arr gives the following information:

ARR BYTE = Array[5, 3]