By default, IDL arrays are passed to and received from the COM subsystem “by value”, meaning the array is copied. When working with large arrays or a large number of arrays, performance may suffer due to the by value passing scheme. However, you can implement “by reference” array passing, which passes an IDL array to a COM object in such a way that the COM object can directly alter the IDL array memory without the cost of marshaling (copying) the array to or from the COM object. This can increase performance and save system memory allocation.
An IDL array parameter is passed by reference to a COM method when the parameter is defined as an IDL pointer to an array. For example:
myarr = LINDGEN(100)
myptr = PTR_NEW(myarr, /NO_COPY)
or
myptr = PTR_NEW(LINDGEN(100), /NO_COPY)
Then, the pointer is passed like a normal parameter:
PRINT, *myptr
obj->UseArrayRef, myptr
PRINT, *myptr
The IDL array must be large enough for the client's use. On the COM side:
- The COM object cannot resize the array (although the COM object does not have to use or set all the elements in the array)
- The COM object cannot change the type of elements
- The COM object cannot change the dimensionality of the array
Thus, for multidimensional arrays, IDL must define the source array with the same dimensions as the COM client expects.
In order for the IDL-COM subsystem to know that an IDL array should be passed by reference, it looks at the source IDL variable to make sure it is a pointer to an array, and that the destination COM method parameter is also declared as an array. Thus, it is important to properly declare the destination COM parameter as a SAFEARRAY(<type>), when implementing in C++.
For example, if the desire is to pass an IDL array of 32-bit integer values to a COM
client, the COM method parameter needs to be declared like this:
[in, out] SAFEARRAY(long) psa
For the code example above, the full method signature in C++/ATL is:
HRESULT UseArrayRef( [in, out] SAFEARRAY(long) psa);
When implementing a COM-callable class in C# and passing in an array of 32-bit integers, declare the method as:
public void UseArrayRef( [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=System.Runtime.InteropServices.VarEnum.VT_I4)] ref long [] arr)
{
arr[0] = 10
arr[1] = 11
// etc
}
It is critical to make sure that the element size of the IDL array matches the element size declared in the COM method signature. If they do not, a marshaling error occurs because the marshaler checks for consistency between the source and destination. This issue is notorious for causing problems with element types of “int” and “long”. For example, trying to call either of the two COM method signatures above with an IDL “integer” array would cause an error since IDL “integers” are 16-bits by default and C++/COM “ints” are 32-bits. Thus, in the code above, we declared the IDL array as “long” values, which are 32-bits and match the C++/COM “long” value in size.
Unsupported Array Types
You cannot pass an array by reference if the array consists of one of the following types:
- Strings
- Object references
- IDL pointers
- IDL structures