• Welcome to PlanetSquires Forums.
 

About Safe Arrays

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

Previous topic - Next topic

José Roca

For the problem of memory leaks with CBSTR and CVARIANT, the solution is to implement overloaded GetElement methods.


' =====================================================================================
' ** BYREF CBSTR ***
' =====================================================================================
' // Multidimensional array
' // prgIndices: first the element, then the dimension
PRIVATE FUNCTION CSafeArray.GetElement (BYVAL prgIndices AS LONG PTR, BYREF cbsData AS CBSTR) AS HRESULT
   CSAFEARRAY_DP("CSafeArray GetElement - multi - CBSTR " & WSTR(cbsData.m_bstr))
   IF m_psa = NULL THEN RETURN E_FAIL
   IF cbsData.m_bstr THEN
      SysFreeString cbsData.m_bstr
      cbsData.m_bstr = NULL
   END IF
   RETURN SafeArrayGetElement(m_psa, prgIndices, @cbsData.m_bstr)
END FUNCTION
' =====================================================================================
' =====================================================================================
' // One-dimensional array
PRIVATE FUNCTION CSafeArray.GetElement (BYVAL idx AS LONG, BYREF cbsData AS CBSTR) AS HRESULT
   CSAFEARRAY_DP("CSafeArray GetElement - 1D - CBSTR " & WSTR(cbsData.m_bstr))
   IF m_psa = NULL THEN RETURN E_FAIL
   IF cbsData.m_bstr THEN
      SysFreeString cbsData.m_bstr
      cbsData.m_bstr = NULL
   END IF
   RETURN SafeArrayGetElement(m_psa, @idx, @cbsData.m_bstr)
END FUNCTION
' =====================================================================================
' =====================================================================================
' // Two-dimensional array
' // First element, then dimension, e.g. 2, 1 (element 2, first dimension), 1, 2 (element 1, 2nd dimension).
PRIVATE FUNCTION CSafeArray.GetElement (BYVAL cElem AS LONG, BYVAL cDim AS LONG, BYREF cbsData AS CBSTR) AS HRESULT
   CSAFEARRAY_DP("CSafeArray GetElement - 2D - CBSTR " & WSTR(cbsData.m_bstr))
   IF m_psa = NULL THEN RETURN E_FAIL
   IF cbsData.m_bstr THEN
      SysFreeString cbsData.m_bstr
      cbsData.m_bstr = NULL
   END IF
   DIM rgIdx(1) AS LONG = {cElem, cDim}
   RETURN SafeArrayGetElement(m_psa, @rgIdx(0), @cbsData.m_bstr)
END FUNCTION
' =====================================================================================


Usage example:


csa.GetElement(1, 1, cbsOut)


Knowing that it is a CBSTR, the function can free the underlying BSTR pointer before passing it to SafeArrayGetElement.

It is needed because SafeArrayGetElement doesn't free the passed BSTR pointer, but overwrites it.


*lpDest = SysAllocStringByteLen((char*)*lpBstr, SysStringByteLen(*lpBstr));


For variants isen't needed because it calls VatiantCopy and this function first clears the destination variant by calling VariantClear.


hRet = VariantCopy(lpDest, lpVariant);


But I will implement overloaded functions for consistency. Otherwise we will have to pass CBSTRs as cbsOut and CVARIANTs as @cvOut.

The scalar types don't need additional overloaded functions.


José Roca

After implementing the additional overloaded PutElement functions, we can use:


DIM cv AS CVARIANT = "Test string 1"
csa.PutElement(1, 1, cv)
cv = 12345
csa.PutElement(2, 1, cv)


and we can also use


csa.PutElement(1, 1, CVARIANT("Test string"))
csa.PutElement(2, 1, CVARIANT(12345))


Similarly, if the safe array is an array of CBSTRs, we can use:


csa.PutElement(1, 1, CBSTR("Test string 1"))
csa.PutElement(2, 1, CBSTR("Test string 2"))


Will play with more overloaded functions until I get it both safe and easy to use.

José Roca

#32
Thanks to the analysis and the revision of the CSafeArray code I have discovered a couple of bugs.

I'm going to implement methods like Append, Insert and Delete, and a Sort method if the safe array is a one-dimensional VT_BSTR safe array. This will make the CBSTRA type obsolete.


' ========================================================================================
' Appends a BSTR to the end of the one-dimensional VT_BSTR safe array.
' If the safe array is not a one-dimensional VT_BSTR array or it is not resizable it will
' return E_FAIL.
' Return value:
'   S_OK Success.
'   DISP_E_BADINDEX The specified index is not valid.
'   E_INVALIDARG One of the arguments is not valid.
'   E_OUTOFMEMORY Memory could not be allocated for the element.
'   E_FAIL The item pointed to by m_psa is not a safe array descriptor.
'          It is a fixed-size array.
'          It is not a one-dimensional array.
' ========================================================================================
PRIVATE FUNCTION CSafeArray.Append (BYREF cbsData AS CBSTR) AS HRESULT
   CSAFEARRAY_DP("CSafeArray Append - CBSTR")
   IF m_psa = NULL THEN RETURN E_FAIL
   DIM nDims AS UINT = SafeArrayGetDim(m_psa)
   IF nDims <> 1 THEN RETURN E_FAIL
   IF this.IsResizable = FALSE THEN RETURN E_FAIL
   DIM vt AS VARTYPE = this.GetType
   IF vt <> VT_BSTR THEN RETURN E_FAIL
   DIM cElements AS DWORD = this.Count(1) + 1
   DIM lLBound AS LONG = this.LBound(1)
   DIM sanewbounds(0) AS SAFEARRAYBOUND = {(cElements, lLBound)}
   DIM hr AS HRESULT = SafeArrayRedim(m_psa, @sanewbounds(0))
   IF hr THEN RETURN hr
   DIM idx AS LONG = cElements - 1 + lLBound
   RETURN SafeArrayPutElement(m_psa, @idx, *cbsData)
END FUNCTION
' ========================================================================================


Usage example:


' // Create a one-dimensional array of BSTR
DIM csa AS CSafeArray = CSafeArray(VT_BSTR, 2, 1)

csa.PutElement(1, CBSTR("Test string 1"))
csa.PutElement(2, CBSTR("Test string 2"))
csa.Append(CBSTR("Test string appended"))

DIM cbsOut AS CBSTR
csa.GetElement(1, cbsOut)
print cbsOut
csa.GetElement(2, cbsOut)
print cbsOut
csa.GetElement(3, cbsOut)
print cbsOut