• Welcome to PlanetSquires Forums.
 

About Safe Arrays

Started by José Roca, September 01, 2016, 12:22:28 PM

Previous topic - Next topic

José Roca

SafeArrayDestroyData destroys all the data in the specified safe array.


HRESULT WINAPI SafeArrayDestroyData ( SAFEARRAY * psa )
{
   HRESULT hr;

   TRACE("(%p)\n", psa);
   
   if (!psa)
     return E_INVALIDARG;

   if (psa->cLocks)
     return DISP_E_ARRAYISLOCKED; /* Can't delete a locked array */

   /* Delete the actual item data */
   hr = SAFEARRAY_DestroyData(psa, 0);
   if (FAILED(hr))
     return hr;

   if (psa->pvData)
   {
     if (psa->fFeatures & FADF_STATIC)
     {
       ZeroMemory(psa->pvData, SAFEARRAY_GetCellCount(psa) * psa->cbElements);
       return S_OK;
     }
     /* If this is not a vector, free the data memory block */
     if (!(psa->fFeatures & FADF_CREATEVECTOR))
     {
       SAFEARRAY_Free(psa->pvData);
       psa->pvData = NULL;
     }
     else
       psa->fFeatures |= FADF_DATADELETED; /* Mark the data deleted */

   }
   return S_OK;
}


It calls the helper internal functions SAFEARRAY_DestroyData and SAFEARRAY_Free, that we have already analyzed.

José Roca

SafeArrayDestroyDescriptor destroys the descriptor of the specified safe array. This function is typically used to destroy the descriptor of a safe array that contains elements with data types other than variants. Destroying the array descriptor does not destroy the elements in the array. Before destroying the array descriptor, call SafeArrayDestroyData to free the elements.


HRESULT WINAPI SafeArrayDestroyDescriptor (SAFEARRAY * psa)

{
   TRACE("(%p)\n", psa);
     
    if (psa)
    {
      LPVOID lpv = (char*)psa - SAFEARRAY_HIDDEN_SIZE;
 
      if (psa->cLocks)
        return DISP_E_ARRAYISLOCKED; /* Can't destroy a locked array */
 
      if (psa->fFeatures & FADF_RECORD)
        SafeArraySetRecordInfo(psa, NULL);
 
      if (psa->fFeatures & FADF_CREATEVECTOR &&
          !(psa->fFeatures & FADF_DATADELETED))
          SAFEARRAY_DestroyData(psa, 0); /* Data not previously deleted */
 
      SAFEARRAY_Free(lpv);
    }
    return S_OK;
  }


José Roca

Please note that if we create the safe arrays with SafeArrayCreate and destroy them with SafeArrayDestroy, we don't have the need of allocating and destroying data and descriptors since the higher level functions do it for us.

These lower-level functions are provided to allow the creation of safe arrays that contain elements with data types other than those provided by SafeArrayCreate.

José Roca

SafeArrayGetDim gets the number of dimensions in the array.


UINT WINAPI SafeArrayGetDim ( SAFEARRAY * psa )
{
   TRACE("(%p) returning %d\n", psa, psa ? psa->cDims : 0u); 
   return psa ? psa->cDims : 0;
}


This function has not complications. It simply returns the value stored in the cDims member.

José Roca

The SafeArrayGetIID function gets the GUID of the interface contained within the specified safe array.


HRESULT WINAPI SafeArrayGetIID ( SAFEARRAY *    psa,
                                 GUID *   pGuid
                               )

{
   GUID* src = (GUID*)psa;

   TRACE("(%p,%p)\n", psa, pGuid);

   if (!psa || !pGuid || !(psa->fFeatures & FADF_HAVEIID))
     return E_INVALIDARG;

   *pGuid = src[-1];
   return S_OK;
}


It first checks the FADF_HAVEIID flag to see if it has an IID and, if it has one, it returns that value, that is stored at a negative offset. Remember that when the memory for the safe array is allocated the value SAFEARRAY_HIDDEN_SIZE (currently 16 bytes, the size of a GUID) is added and the returned pointer to the safe array data doesn't point to the beginning of the allocated memory, but to ptr + SAFEARRAY_HIDDEN_SIZE).


char *ptr = SAFEARRAY_Malloc(ulSize + SAFEARRAY_HIDDEN_SIZE);
*ppsaOut = (SAFEARRAY*)(ptr + SAFEARRAY_HIDDEN_SIZE);


José Roca

SafeArrayGetLBound gets the lower bound for any dimension of the specified safe array.


HRESULT WINAPI SafeArrayGetLBound ( SAFEARRAY * psa,
                                    UINT        nDim,
                                    LONG *      plLbound
                                  ) 
{
   TRACE("(%p,%d,%p)\n", psa, nDim, plLbound);

   if (!psa || !plLbound)
     return E_INVALIDARG;

   if(!nDim || nDim > psa->cDims)
     return DISP_E_BADINDEX;

   *plLbound = psa->rgsabound[psa->cDims - nDim].lLbound;
   return S_OK;
}


SafeArrayGetUbound gets the upper bound for any dimension of the specified safe array.


HRESULT WINAPI SafeArrayGetUBound ( SAFEARRAY * psa,
                                    UINT        nDim,
                                    LONG *      plUbound
                                  )
{
   TRACE("(%p,%d,%p)\n", psa, nDim, plUbound);
     
   if (!psa || !plUbound)
     return E_INVALIDARG;

   if(!nDim || nDim > psa->cDims)
     return DISP_E_BADINDEX;

   *plUbound = psa->rgsabound[psa->cDims - nDim].lLbound +
               psa->rgsabound[psa->cDims - nDim].cElements - 1;

   return S_OK;
}


José Roca

SafeArrayGetVartype gets the VARTYPE stored in the specified safe array.

If FADF_HAVEVARTYPE is set, SafeArrayGetVartype returns the VARTYPE stored in the array descriptor. If FADF_RECORD is set, it returns VT_RECORD; if FADF_DISPATCH is set, it returns VT_DISPATCH; and if FADF_UNKNOWN is set, it returns VT_UNKNOWN.



HRESULT WINAPI SafeArrayGetVartype ( SAFEARRAY * psa,
                                     VARTYPE *   pvt
                                   )
{
   TRACE("(%p,%p)\n", psa, pvt);

   if (!psa || !pvt)
     return E_INVALIDARG;

   if (psa->fFeatures & FADF_RECORD)
     *pvt = VT_RECORD;
   else if ((psa->fFeatures & (FADF_HAVEIID|FADF_DISPATCH)) == (FADF_HAVEIID|FADF_DISPATCH))
     *pvt = VT_DISPATCH;
   else if (psa->fFeatures & FADF_HAVEIID)
     *pvt = VT_UNKNOWN;
   else if (psa->fFeatures & FADF_HAVEVARTYPE)
   {
     VARTYPE vt = SAFEARRAY_GetHiddenDWORD(psa);
     *pvt = vt;
   }
   else
     return E_INVALIDARG;

   return S_OK;
}



static DWORD SAFEARRAY_GetHiddenDWORD  (  SAFEARRAY *    psa   )
{
    LPDWORD lpDw = (LPDWORD)psa;
    return lpDw[-1];
  }
[code]

In the above code we can see a problem:

[code]
   else if (psa->fFeatures & FADF_HAVEIID)
     *pvt = VT_UNKNOWN;


While the code for VT_DISPATCH


if ((psa->fFeatures & (FADF_HAVEIID|FADF_DISPATCH)) == (FADF_HAVEIID|FADF_DISPATCH))


checks for FADF_HAVEIID|FADF_DISPATCH, the one for VT_UNKNOWN doesn't check for


if ((psa->fFeatures & (FADF_HAVEIID|FADF_UNKNOWN)) == (FADF_HAVEIID|FADF_UNKNOWN))


but assumes that if it has an IID and it is not DISPATCH, it must be UNKNOWN.

Therefore, SafeArrayGetVartype can fail to return VT_UNKNOWN for SAFEARRAY types that are based on IUnknown. Callers should additionally check whether the SAFEARRAY type's fFeatures field has the FADF_UNKNOWN flag set.

But the safe array API doesn't provide a SafearrayGetFeatures function, so I will have to implement an additional method to the CSafeArrayClass to return it.

José Roca

SafeArrayGetIID gets the GUID of the interface contained within the specified safe array and SafeArraySetIID sets the GUID of the interface for the specified safe array. It first checks if the safe array has the FADF_HAVEIID flag.


HRESULT WINAPI SafeArrayGetIID ( SAFEARRAY *    psa,
                                 GUID *         pGuid
                               )
{
   GUID* src = (GUID*)psa;

   TRACE("(%p,%p)\n", psa, pGuid);

   if (!psa || !pGuid || !(psa->fFeatures & FADF_HAVEIID))
     return E_INVALIDARG;

   *pGuid = src[-1];
   return S_OK;
}



HRESULT WINAPI SafeArraySetIID ( SAFEARRAY * psa,
                                 REFGUID     guid
                                 )
{
   GUID* dest = (GUID*)psa;

   TRACE("(%p,%s)\n", psa, debugstr_guid(guid));

   if (!psa || !guid || !(psa->fFeatures & FADF_HAVEIID))
     return E_INVALIDARG;

   dest[-1] = *guid;
   return S_OK;
}


José Roca

SafearrayGetRecordInfo retrieves the IRecordInfo interface of the UDT contained in the specified safe array and SafearraySetRecordInfo sets the record info in the specified safe array. It first checks if the safe array has the FADF_RECORD flag.


HRESULT WINAPI SafeArrayGetRecordInfo ( SAFEARRAY *    psa,
                                        IRecordInfo ** pRinfo
                                      )
{
   IRecordInfo** src = (IRecordInfo**)psa;

   TRACE("(%p,%p)\n", psa, pRinfo);

   if (!psa || !pRinfo || !(psa->fFeatures & FADF_RECORD))
     return E_INVALIDARG;

   *pRinfo = src[-1];

   if (*pRinfo)
     IRecordInfo_AddRef(*pRinfo);
   return S_OK;
}



HRESULT WINAPI SafeArraySetRecordInfo ( SAFEARRAY *    psa,
                                        IRecordInfo *  pRinfo
                                      )
{
   IRecordInfo** dest = (IRecordInfo**)psa;

   TRACE("(%p,%p)\n", psa, pRinfo);
   
   if (!psa || !(psa->fFeatures & FADF_RECORD))
     return E_INVALIDARG;

   if (pRinfo)
     IRecordInfo_AddRef(pRinfo);

   if (dest[-1])
     IRecordInfo_Release(dest[-1]);

   dest[-1] = pRinfo;
   return S_OK;
}




José Roca

SafeArrayAccessData increments the lock count of an array, and retrieves a pointer to the array data. This allows us to access directly to the data using pointers, which is the fatest way of doing it instead of calling SafeArrayGetElement, but we have the entire responsability on what we do.

SafeArrayUnaccessData simply unlocks the array.


HRESULT WINAPI SafeArrayAccessData ( SAFEARRAY * psa,
                                     void **     ppvData
                                    )
{
   HRESULT hr;

   TRACE("(%p,%p)\n", psa, ppvData);

   if(!psa || !ppvData)
     return E_INVALIDARG;

   hr = SafeArrayLock(psa);
   *ppvData = SUCCEEDED(hr) ? psa->pvData : NULL;

   return hr;
}



HRESULT WINAPI SafeArrayUnaccessData ( SAFEARRAY * psa )

{
   TRACE("(%p)\n", psa);
   return SafeArrayUnlock(psa);
}


José Roca

SafeArrayGetElemsize returns the size of an element in a safe array, in bytes.


UINT WINAPI SafeArrayGetElemsize ( SAFEARRAY * psa )
{
  TRACE("(%p) returning %d\n", psa, psa ? psa->cbElements : 0u);
  return psa ? psa->cbElements : 0;
}


José Roca

Finally, SafeArrayCreateVector and SafeArrayCreateVectorEx create a one-dimensional array.

However, there is something that I don't understand. The MSDN documentation states that a safe array created with SafeArrayCreateVector/Ex is a fixed size, so the constant FADF_FIXEDSIZE is always set, but in the source code I don't see anywhere that FADF_FIXEDSIZE is set. Furthemore, it is in the list of ignored flags:


const USHORT ignored_copy_features
   static
Initial value:
=
        FADF_AUTO |
        FADF_STATIC |
        FADF_EMBEDDED |
        FADF_FIXEDSIZE |
        FADF_CREATEVECTOR


So I wonder how can we ascertain if we are dealing with a one-dimensional array created with SafeArrayCreate or a vector (?).


SAFEARRAY* WINAPI SafeArrayCreateVector ( VARTYPE  vt,
                                          LONG     lLbound,
                                          ULONG    cElements
                                        )
{
   TRACE("(%d->%s,%d,%d\n", vt, debugstr_vt(vt), lLbound, cElements);
     
   if (vt == VT_RECORD)
     return NULL;

   return SAFEARRAY_CreateVector(vt, lLbound, cElements, SAFEARRAY_GetVTSize(vt));
}



static SAFEARRAY* SAFEARRAY_CreateVector ( VARTYPE     vt,
                                           LONG     lLbound,
                                           ULONG    cElements,
                                           ULONG    ulSize
                                         )

{
   SAFEARRAY *psa = NULL;

   if (ulSize || (vt == VT_RECORD))
   {
     /* Allocate the header and data together */
     if (SUCCEEDED(SAFEARRAY_AllocDescriptor(sizeof(SAFEARRAY) + ulSize * cElements, &psa)))
     {
       SAFEARRAY_SetFeatures(vt, psa);

       psa->cDims = 1;
       psa->fFeatures |= FADF_CREATEVECTOR;
       psa->pvData = &psa[1]; /* Data follows the header */
       psa->cbElements = ulSize;
       psa->rgsabound[0].cElements = cElements;
       psa->rgsabound[0].lLbound = lLbound;

       switch (vt)
       {
         case VT_BSTR:     psa->fFeatures |= FADF_BSTR; break;
         case VT_UNKNOWN:  psa->fFeatures |= FADF_UNKNOWN; break;
         case VT_DISPATCH: psa->fFeatures |= FADF_DISPATCH; break;
         case VT_VARIANT:  psa->fFeatures |= FADF_VARIANT; break;
       }
     }
   }
   return psa;
}


The code is very similar to the one used by SafeArrayCreate with the important exception that the pvData pointer is set one byte after the header.


psa->pvData = &psa[1]; /* Data follows the header */


SafeArrayCreateVectorEx adds an additional parameter, pvExtra, to set the type information of the user-defined type, if you are creating a safe array of user-defined types. If the vt parameter is VT_RECORD, then pvExtra will be a pointer to an IRecordInfo describing the record. If the vt parameter is VT_DISPATCH or VT_UNKNOWN, then pvExtra will contain a pointer to a GUID representing the type of interface being passed to the array.


SAFEARRAY* WINAPI SafeArrayCreateVectorEx ( VARTYPE  vt,
                                            LONG     lLbound,
                                            ULONG    cElements,
                                            LPVOID   pvExtra
                                          )
{
   ULONG ulSize;
   IRecordInfo* iRecInfo = pvExtra;
   SAFEARRAY* psa;

  TRACE("(%d->%s,%d,%d,%p\n", vt, debugstr_vt(vt), lLbound, cElements, pvExtra);
 
   if (vt == VT_RECORD)
   {
     if  (!iRecInfo)
       return NULL;
     IRecordInfo_GetSize(iRecInfo, &ulSize);
   }
   else
     ulSize = SAFEARRAY_GetVTSize(vt);

   psa = SAFEARRAY_CreateVector(vt, lLbound, cElements, ulSize);

   if (pvExtra)
   {
     switch(vt)
     {
       case VT_RECORD:
         SafeArraySetRecordInfo(psa, iRecInfo);
         break;
       case VT_UNKNOWN:
       case VT_DISPATCH:
         SafeArraySetIID(psa, pvExtra);
         break;
     }
   }
   return psa;
}


José Roca

To get the size of the element, SafeArrayCreateVectorEx calls the helper internan function SAFEARRAY_GetVTSize.


static DWORD SAFEARRAY_GetVTSize ( VARTYPE vt )
{
   switch (vt)
   {
     case VT_I1:
     case VT_UI1:      return sizeof(BYTE);
     case VT_BOOL:
     case VT_I2:
     case VT_UI2:      return sizeof(SHORT);
     case VT_I4:
     case VT_UI4:
     case VT_R4:
     case VT_ERROR:    return sizeof(LONG);
     case VT_R8:
     case VT_I8:
     case VT_UI8:      return sizeof(LONG64);
     case VT_INT:
     case VT_UINT:     return sizeof(INT);
     case VT_INT_PTR:
     case VT_UINT_PTR: return sizeof(UINT_PTR);
     case VT_CY:       return sizeof(CY);
     case VT_DATE:     return sizeof(DATE);
     case VT_BSTR:     return sizeof(BSTR);
     case VT_DISPATCH: return sizeof(LPDISPATCH);
     case VT_VARIANT:  return sizeof(VARIANT);
     case VT_UNKNOWN:  return sizeof(LPUNKNOWN);
     case VT_DECIMAL:  return sizeof(DECIMAL);
     /* Note: Return a non-zero size to indicate vt is valid. The actual size
      * of a UDT is taken from the result of IRecordInfo_GetSize().
      */
     case VT_RECORD:   return 32;
   }
   return 0;
}


José Roca

#28
There are two additional API functions, VectorFromBstr and BstrFromVector.

VectorFromBstr returns a vector, assigning each character in the BSTR to an element of the vector.

BstrFromVector returns a BSTR, assigning each element of the vector to a character in the BSTR.


HRESULT WINAPI VectorFromBstr ( BSTR     bstr,
                                SAFEARRAY **   ppsa
                              )
{
   SAFEARRAYBOUND sab;

   TRACE("(%p,%p)\n", bstr, ppsa);
   
   if (!ppsa)
     return E_INVALIDARG;

   sab.lLbound = 0;
   sab.cElements = SysStringByteLen(bstr);

   *ppsa = SAFEARRAY_Create(VT_UI1, 1, &sab, 0);

   if (*ppsa)
   {
     memcpy((*ppsa)->pvData, bstr, sab.cElements);
     return S_OK;
   }
   return E_OUTOFMEMORY;
}



HRESULT WINAPI BstrFromVector ( SAFEARRAY * psa,
BSTR * pbstr
)

{
   TRACE("(%p,%p)\n", psa, pbstr);

   if (!pbstr)
   return E_INVALIDARG;

   *pbstr = NULL;

   if (!psa || psa->cbElements != 1 || psa->cDims != 1)
   return E_INVALIDARG;

   *pbstr = SysAllocStringByteLen(psa->pvData, psa->rgsabound[0].cElements);
   if (!*pbstr)
   return E_OUTOFMEMORY;
   return S_OK;
}


Again, I don't see the FADF_FIXEDSIZE flags anywhere. What VectorFromBstr is doing is to create a safe array of unsigned bytes.

José Roca

The anlysis is completed. Now that I know how the safe array API works internally I will see what I can do to improve the CSafeArray wrapper class.