11

how to iterate through C++ safearray pointer to pointer and access its elements.

I tried to replicate the solution posted by Lim Bio Liong http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/022dba14-9abf-4872-9f43-f4fc05bd2602 but the strangest thing is that the IDL method signature comes out to be

HRESULT __stdcall GetTestStructArray([out] SAFEARRAY ** test_struct_array);

instead of

HRESULT __stdcall GetTestStructArray([out] SAFEARRAY(TestStruct)* test_struct_array);

Any ideas?

thanks in advance

Dmytro
  • 16,668
  • 27
  • 80
  • 130
TrustyCoder
  • 4,749
  • 10
  • 66
  • 119

2 Answers2

24

Safearrays are created with SafeArrayCreate or SafeArrayCreateVector, but as you ask about iterating over a SAFEARRAY, let's say you already have a SAFEARRAY returned by some other function. One way is to use SafeArrayGetElement API which is especially convenient if you have multidimensional SAFEARRAYs, as it allows, IMO, a bit easier specifying of the indices.

However, for vectors (unidimensional SAFEARRAY) it is faster to access data directly and iterate over the values. Here's an example:

Let's say it's a SAFEARRAY of longs, ie. VT_I4

// get them from somewhere. (I will assume that this is done 
// in a way that you are now responsible to free the memory)
SAFEARRAY* saValues = ... 
LONG* pVals;
HRESULT hr = SafeArrayAccessData(saValues, (void**)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
  long lowerBound, upperBound;  // get array bounds
  SafeArrayGetLBound(saValues, 1 , &lowerBound);
  SafeArrayGetUBound(saValues, 1, &upperBound);

  long cnt_elements = upperBound - lowerBound + 1; 
  for (int i = 0; i < cnt_elements; ++i)  // iterate through returned values
  {                              
    LONG lVal = pVals[i];   
    std::cout << "element " << i << ": value = " << lVal << std::endl;
  }       
  SafeArrayUnaccessData(saValues);
}
SafeArrayDestroy(saValues);
Zdeslav Vojkovic
  • 14,391
  • 32
  • 45
  • 6
    SafeArrayDestroy() doesn't belong in this code. Don't destroy the array if you don't own it. – Hans Passant Sep 19 '12 at 15:43
  • 1
    the `get them from somewhere` was meant to denote a typical scenario of having a method returning a SAFEARRAY as `[out]` parameter in which case the client is responsible for cleanup, but that might not be obvious. I have edited the answer – Zdeslav Vojkovic Sep 19 '12 at 15:52
  • How do I access Safearray when I have 2 dimensions? – savi Oct 01 '14 at 23:27
  • @Zdeslav: Hans Passant is right. Imagine that the caller of your function is passing the same SAFEARRAY afterwards to another function to do other things with the data! The code will fail because it is a design error that a function destroys data that is passed as input parameter. It must be the caller of your function who cares about destroying the SAFEARRAY. If each function destroys only its own data you will never get problems. Take this as a design rule written in stone. If the SAFEARRAY comes from a Windows API you could store it in a COleSafeArray so the destructor destroys it. – Elmue Jan 20 '17 at 12:38
4

MSDN SafeArrayGetElement function gives you a code snippet on using SafeArrayGetElement to obtain individual object to array.

SAFEARRAY structure and SafeArray* functions explain the available API.

In ATL/MFC project you would want to use wrapper classes e.g. CComSafeArray to make things simpler and easier. See Simplifying SAFEARRAY programming with CComSafeArray on this.

user4035
  • 22,508
  • 11
  • 59
  • 94
Roman R.
  • 68,205
  • 6
  • 94
  • 158