News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Everything You Never Wanted to Know About SAFEARRAYs

Started by Zen, June 25, 2016, 07:10:52 AM

Previous topic - Next topic

Zen

Yes,...SAFEARRAYs,...how does Microsoft come up with such a cumbersome object ???
Here is the Official MSDN Documentation: SAFEARRAY Structure, MSDN

Quote from: SAFEARRAYs Made Easier"The SAFEARRAY data type was designed to be used in Automation to pass multidimensional arrays to and from COM interface methods. This type is a structure that contains boundary information as well as a reference to the data. This allows the passing of arrays between COM Apartments and/or processes without the need to implement a marshaler for custom COM interfaces. The SAFEARRAY type, being automation-compatible itself, can only consist of automation-compatible data."
Above quote from: SAFEARRAYs Made Easier, By: Andrew Ames

...And, here's the basic method of accessing SAFEARRAY data (in C++, of course): How To Access Data Inside the SAFEARRAY

This is the SAFEARRAY Structure definition from Japheth's OAIDL.INC:   
;---------- SAFEARRAY ----------------------------------

SAFEARRAYBOUND struct dword
cElements   dword       ?
lLbound     sdword      ?
SAFEARRAYBOUND ends

LPSAFEARRAYBOUND typedef ptr SAFEARRAYBOUND

SAFEARRAY   struct dword
    cDims       word        ?
    fFeatures   word        ?
    cbElements  dword       ?
    cLocks      dword       ?
    pvData      PVOID       ?
    rgsabound   SAFEARRAYBOUND      1t DUP (<>)
SAFEARRAY ENDS

;---------- SAFEARRAY ----------------------------------

LPSAFEARRAY     typedef ptr SAFEARRAY


And, these are the OFFICIAL Microsoft SAFEARRAY Functions: Array Manipulation Functions, MSDN

...So,...why aren't there any COMPLETELY FOOLPROOF MASM SAFEARRAY macros ???
Zen

Zen

(The following is from: Essential COM, by Don Box)   

SAFEARRAY is a fairly unusual data structure that allows multidimensional arrays of VARIANT-compatible types to be passed as parameters. To express the dimensions of a SAFEARRAY, COM provides the SAFEARRAYBOUND data type:

typedef struct tagSAFEARRAYBOUND {
  ULONG cElements;    // size_is for dimension 
  LONG  lLbound;    //  minimum index for dimension (usually zero)
} SAFEARRAYBOUND, *LPSAFEARRAYBOUND;


The SAFEARRAY data type internally uses a conformant array of SAFEARRAYBOUNDs to put some shape around the array's contents:   

typedef struct tagSAFEARRAY {
  USHORT         cDims;    //  number of dimensions
  USHORT         fFeatures;    //  Flags describing contents 
  ULONG          cbElements;    //  number of bytes per element 
  ULONG          cLocks;    //  used to track memory usage 
  PVOID          pvData;    //  actual elements 
  SAFEARRAYBOUND rgsabound[1];
} SAFEARRAY, *LPSAFEARRAY;


The preceeding IDL is not actually used to describe the wire format of SAFEARRAYs, but it is used to describe them programmatically. 
To allow the user maximum flexibility with respect to memory management, COM defines the following flags that can be used with the fFeatures field: 

FADF_AUTO      = array is allocated on the stack 
FADF_STATIC    = array is statically allocated   
FADF_EMBEDDED  = array is embedded in a structure 
FADF_FIXEDSIZE = may not be resized or reallocated   
FADF_BSTR      = an array of BSTRs 
FADF_UNKNOWN   = an array of IUnknown* 
FADF_DISPATCH  = an array of IDispatch* 
FADF_VARIANT   = an array of VARIANTs 

To allow the data type of the SAFEARRAYs elements to be specified, the IDL compiler recognizes a special SAFEARRAY-specific syntax:

    HRESULT Method([in] SAFEARRAY(type) *ppsa);   

where type is the type of element in the SAFEARRAY. The corresponding C++ method prototype would look like this: 

    HRESULT Method(SAFEARRAY **ppsa); 

Note that only one level of indirection is used in the IDL definition; however, two levels of indirection are used in the corresponding C++ definition.
The SAFEARRAY data type is supported by a fairly rich API set that allows arrays to be redimensioned and traversed portably. For accessing the elements of a SAFEARRAY, COM provides the following API calls:   

//  get a pointer to the actual array elements 
    HRESULT SafeArrayAccessData([in] SAFEARRAY *psa, [out]void **ppv);   
//  release pointer returned by SafeArrayAccessData   
    HRESULT SafeArrayUnaccessData([in]SAFEARRAY *psa);    
//  get number of dimensions   
    HRESULT SafeArrayGetDim([in]SAFEARRAY *psa);    
//  get upper bound of a dimension   
    HRESULT SafeArrayGetUBound([in]SAFEARRAY *psa, [in] UINT nDIM, [OUT]long *pUBound);  
//  get lower bound of a dimension   
    HRESULT SafeArrayGetLBound([in]SAFEARRAY *psa, [in] UINT nDIM, [OUT]long *pLBound);  

These methods provide a portable and safe method for accessing the actual contents of the array. Assuming the following IDL:   

    HRESULT Sum([in]SAFEARRAY(long) *ppsa, [out, retval] long *pSum);   

the following implementation calculates the sum of a SAFEARRAY of long integers:   

    STDMETHODIMP MyClass::Sum(SAFEARRAY **ppsa, long *pnSum) {   
        assert(ppsa && *ppsa && pnSum);   
        assert (SafeArrayGetDim(*ppsa) == 1);   
        long iUBound, iLBound;   
    //  note that dimension indices are one-based   
        HRESULT hr = SafeArrayGetUBound(*ppsa, 1, &iUBound);   
        assert (SUCCEEDED(hr));   
        hr = SafeArrayGetLBound(*ppsa, 1, &iLBound);   
        assert (SUCCEEDED(hr));   
        long *prgn = 0;   
        hr = SafeArrayAccessData(*ppsa, (void**)&prgn);   
        *pnSum = 0;   
        for (long intgr = iLBound; intgr <= iUBound; intgr++)   
            *pnSum += prgn[intgr];   
        SafeArrayUnaccessData(*ppsa);   
        return S_OK;   
        }   

Note that any API calls that deal with the dimensions of the array use one-based indices.   
The preceeding code fragment simply manipulated the contents of an existing SAFEARRAY. To create a one-dimensional SAFEARRAY to pass as a method parameter, COM provides the following API function that allocates the memory for the SAFEARRAY structure and its array elements in one contiguous block of memory:   

    SAFEARRAY* SafeArrayCreateVector(
            [in] VARTYPE vt,    //  element type 
            [in] long lLbound,    //  index of the lower bound 
            [in] unsigned int cElems);    //  number of elements 


Zen

jj2007

Quote from: Zen on June 25, 2016, 07:10:52 AM...So,...why aren't there any COMPLETELY FOOLPROOF MASM SAFEARRAY macros ???

You would be surprised how safe MasmBasic arrays are :bgrin: