wine/ole/safearray.c
1999-04-25 19:01:52 +00:00

998 lines
30 KiB
C

/*************************************************************************
* OLE Automation
* SafeArray Implementation
*
* This file contains the implementation of the SafeArray interface.
*
* Copyright 1999 Sylvain St-Germain
*/
#include <stdio.h>
#include <string.h>
#include "windef.h"
#include "winerror.h"
#include "winbase.h"
#include "oleauto.h"
#include "wine/obj_base.h"
#include "debug.h"
DEFAULT_DEBUG_CHANNEL(ole)
/* Localy used methods */
static INT
endOfDim(LONG *coor, SAFEARRAYBOUND *mat, LONG dim, LONG realDim);
static ULONG
calcDisplacement(LONG *coor, SAFEARRAYBOUND *mat, LONG dim);
static BOOL
isPointer(USHORT feature);
static INT
getFeatures(VARTYPE vt);
static BOOL
validCoordinate(LONG *coor, SAFEARRAY *psa);
static BOOL
resizeSafeArray(SAFEARRAY *psa, LONG lDelta);
static BOOL
validArg(SAFEARRAY *psa);
static ULONG
getArraySize(SAFEARRAY *psa);
static HRESULT
duplicateData(SAFEARRAY *psa, SAFEARRAY **ppsaOut);
/* Association between VARTYPE and their size.
A size of zero is defined for the unsupported types. */
#define VARTYPE_NOT_SUPPORTED 0
const static ULONG VARTYPE_SIZE[] =
{
/* this is taken from wtypes.h. Only [S]es are supported by the SafeArray */
VARTYPE_NOT_SUPPORTED, /* VT_EMPTY [V] [P] nothing */
VARTYPE_NOT_SUPPORTED, /* VT_NULL [V] [P] SQL style Nul */
2, /* VT_I2 [V][T][P][S] 2 byte signed int */
4, /* VT_I4 [V][T][P][S] 4 byte signed int */
4, /* VT_R4 [V][T][P][S] 4 byte real */
8, /* VT_R8 [V][T][P][S] 8 byte real */
8, /* VT_CY [V][T][P][S] currency */
8, /* VT_DATE [V][T][P][S] date */
4, /* VT_BSTR [V][T][P][S] OLE Automation string*/
4, /* VT_DISPATCH [V][T][P][S] IDispatch * */
4, /* VT_ERROR [V][T] [S] SCODE */
4, /* VT_BOOL [V][T][P][S] True=-1, False=0*/
24, /* VT_VARIANT [V][T][P][S] VARIANT * */
4, /* VT_UNKNOWN [V][T] [S] IUnknown * */
16, /* VT_DECIMAL [V][T] [S] 16 byte fixed point */
VARTYPE_NOT_SUPPORTED, /* no VARTYPE here..... */
VARTYPE_NOT_SUPPORTED, /* VT_I1 [T] signed char */
1, /* VT_UI1 [V][T][P][S] unsigned char */
VARTYPE_NOT_SUPPORTED, /* VT_UI2 [T][P] unsigned short */
VARTYPE_NOT_SUPPORTED, /* VT_UI4 [T][P] unsigned short */
VARTYPE_NOT_SUPPORTED, /* VT_I8 [T][P] signed 64-bit int */
VARTYPE_NOT_SUPPORTED, /* VT_UI8 [T][P] unsigned 64-bit int */
VARTYPE_NOT_SUPPORTED, /* VT_INT [T] signed machine int */
VARTYPE_NOT_SUPPORTED, /* VT_UINT [T] unsigned machine int */
VARTYPE_NOT_SUPPORTED, /* VT_VOID [T] C style void */
VARTYPE_NOT_SUPPORTED, /* VT_HRESULT [T] Standard return type */
VARTYPE_NOT_SUPPORTED, /* VT_PTR [T] pointer type */
VARTYPE_NOT_SUPPORTED, /* VT_SAFEARRAY [T] (use VT_ARRAY in VARIANT)*/
VARTYPE_NOT_SUPPORTED, /* VT_CARRAY [T] C style array */
VARTYPE_NOT_SUPPORTED, /* VT_USERDEFINED [T] user defined type */
VARTYPE_NOT_SUPPORTED, /* VT_LPSTR [T][P] null terminated string */
VARTYPE_NOT_SUPPORTED, /* VT_LPWSTR [T][P] wide null term string */
VARTYPE_NOT_SUPPORTED, /* VT_FILETIME [P] FILETIME */
VARTYPE_NOT_SUPPORTED, /* VT_BLOB [P] Length prefixed bytes */
VARTYPE_NOT_SUPPORTED, /* VT_STREAM [P] Name of stream follows */
VARTYPE_NOT_SUPPORTED, /* VT_STORAGE [P] Name of storage follows */
VARTYPE_NOT_SUPPORTED, /* VT_STREAMED_OBJECT[P] Stream contains an object*/
VARTYPE_NOT_SUPPORTED, /* VT_STORED_OBJECT [P] Storage contains object*/
VARTYPE_NOT_SUPPORTED, /* VT_BLOB_OBJECT [P] Blob contains an object*/
VARTYPE_NOT_SUPPORTED, /* VT_CF [P] Clipboard format */
VARTYPE_NOT_SUPPORTED, /* VT_CLSID [P] A Class ID */
VARTYPE_NOT_SUPPORTED, /* VT_VECTOR [P] simple counted array */
VARTYPE_NOT_SUPPORTED, /* VT_ARRAY [V] SAFEARRAY* */
VARTYPE_NOT_SUPPORTED /* VT_BYREF [V] void* for local use */
};
const static int LAST_VARTYPE = sizeof(VARTYPE_SIZE)/sizeof(ULONG);
/*************************************************************************
* Allocate the appropriate amount of memory for the SafeArray descriptor
*/
HRESULT WINAPI SafeArrayAllocDescriptor(
UINT cDims,
SAFEARRAY **ppsaOut)
{
SAFEARRAYBOUND *sab;
LONG allocSize = 0;
/* SAFEARRAY + SAFEARRAYBOUND * (cDims -1) ( -1 because there is already one
( in SAFEARRAY struct */
allocSize = sizeof(**ppsaOut) + (sizeof(*sab) * (cDims-1));
/* Allocate memory for SAFEARRAY struc */
if(( (*ppsaOut)=HeapAlloc(
GetProcessHeap(), HEAP_ZERO_MEMORY, allocSize)) == NULL){
return(E_UNEXPECTED);
}
TRACE(ole,"SafeArray: %lu bytes allocated for descriptor.\n", allocSize);
return(S_OK);
}
/*************************************************************************
* Allocate the appropriate amount of data for the SafeArray data
*/
HRESULT WINAPI SafeArrayAllocData(
SAFEARRAY *psa)
{
ULONG ulWholeArraySize; /* to store the size of the whole thing */
if(! validArg(psa))
return E_INVALIDARG;
ulWholeArraySize = getArraySize(psa);
/* Allocate memory for the data itself */
if((psa->pvData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
psa->cbElements*ulWholeArraySize)) == NULL)
return(E_UNEXPECTED);
TRACE(ole, "SafeArray: %lu bytes allocated for data at %p (%lu objects).\n",
psa->cbElements*ulWholeArraySize, psa->pvData, ulWholeArraySize);
return(S_OK);
}
/*************************************************************************
* Create a SafeArray object by encapsulating AllocDescriptor and AllocData
*/
SAFEARRAY* WINAPI SafeArrayCreate(
VARTYPE vt,
UINT cDims,
SAFEARRAYBOUND *rgsabound)
{
SAFEARRAY *psa;
HRESULT hRes;
USHORT cDim;
/* Validate supported VARTYPE */
if ( (vt >= LAST_VARTYPE) ||
( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
return NULL;
/* Allocate memory for the array descriptor */
if( FAILED( hRes = SafeArrayAllocDescriptor(cDims, &psa)))
return NULL;
/* setup data members... */
psa->cDims = cDims;
psa->fFeatures = getFeatures(vt);
psa->cLocks = 0;
psa->pvData = NULL;
psa->cbElements= VARTYPE_SIZE[vt];
/* Invert the bounds ... */
for(cDim=0; cDim < psa->cDims; cDim++) {
psa->rgsabound[cDim].cElements = rgsabound[psa->cDims-cDim-1].cElements;
psa->rgsabound[cDim].lLbound = rgsabound[psa->cDims-cDim-1].lLbound;
}
/* allocate memory for the data... */
if( FAILED( hRes = SafeArrayAllocData(psa))) {
SafeArrayDestroyDescriptor(psa);
ERR(ole,"() : Failed to allocate the Safe Array data\n");
return NULL;
}
return(psa);
}
/*************************************************************************
* Frees the memory associated with the descriptor.
*/
HRESULT WINAPI SafeArrayDestroyDescriptor(
SAFEARRAY *psa)
{
/* Check for lockness before to free... */
if(psa->cLocks > 0)
return DISP_E_ARRAYISLOCKED;
/* The array is unlocked, then, deallocate memory */
if(HeapFree( GetProcessHeap(), 0, psa) == FALSE)
return E_UNEXPECTED;
return(S_OK);
}
/*************************************************************************
* Increment the lock counter
*
* Doc says (MSDN Library ) that psa->pvData should be made available (!= NULL)
* only when psa->cLocks is > 0... I don't get it since pvData is allocated
* before the array is locked, therefore
*/
HRESULT WINAPI SafeArrayLock(
SAFEARRAY *psa)
{
if(! validArg(psa))
return E_INVALIDARG;
psa->cLocks++;
return(S_OK);
}
/*************************************************************************
* Decrement the lock counter
*/
HRESULT WINAPI SafeArrayUnlock(
SAFEARRAY *psa)
{
if(! validArg(psa))
return E_INVALIDARG;
if (psa->cLocks > 0)
psa->cLocks--;
return(S_OK);
}
/*************************************************************************
* Set the data at the given coordinate
*/
HRESULT WINAPI SafeArrayPutElement(
SAFEARRAY *psa,
LONG *rgIndices,
void *pv)
{
ULONG stepCountInSAData = 0; /* Number of array item to skip to get to
the desired one... */
PVOID elementStorageAddress = NULL; /* Adress to store the data */
BSTR pbstrReAllocStr = NULL; /* BSTR reallocated */
/* Validate the index given */
if(! validCoordinate(rgIndices, psa))
return DISP_E_BADINDEX;
if(! validArg(psa))
return E_INVALIDARG;
if( SafeArrayLock(psa) == S_OK) {
/* Figure out the number of items to skip */
stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
/* Figure out the number of byte to skip ... */
elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
if(isPointer(psa->fFeatures)) { /* increment ref count for this pointer */
*((VOID**)elementStorageAddress) = *(VOID**)pv;
IUnknown_AddRef( *(IUnknown**)pv);
} else {
if(psa->fFeatures == FADF_BSTR) { /* Create a new object */
if((pbstrReAllocStr = SysAllocString( (OLECHAR*)pv )) == NULL) {
SafeArrayUnlock(psa);
return E_OUTOFMEMORY;
} else
*((BSTR*)elementStorageAddress) = pbstrReAllocStr;
} else /* dupplicate the memory */
memcpy(elementStorageAddress, pv, SafeArrayGetElemsize(psa) );
}
} else {
ERR(ole, "SafeArray: Cannot lock array....\n");
return E_UNEXPECTED; /* UNDOC error condition */
}
TRACE(ole,"SafeArray: item put at adress %p.\n",elementStorageAddress);
return SafeArrayUnlock(psa);
}
/*************************************************************************
* Return the data element corresponding the the given coordinate
*/
HRESULT WINAPI SafeArrayGetElement(
SAFEARRAY *psa,
LONG *rgIndices,
void *pv)
{
ULONG stepCountInSAData = 0; /* Number of array item to skip to get to
the desired one... */
PVOID elementStorageAddress = NULL; /* Adress to store the data */
BSTR pbstrReturnedStr = NULL; /* BSTR reallocated */
if(! validArg(psa))
return E_INVALIDARG;
if(! validCoordinate(rgIndices, psa)) /* Validate the index given */
return(DISP_E_BADINDEX);
if( SafeArrayLock(psa) == S_OK) {
/* Figure out the number of items to skip */
stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
/* Figure out the number of byte to skip ... */
elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
if( psa->fFeatures == FADF_BSTR) { /* reallocate the obj */
if( (pbstrReturnedStr =
SysAllocString( *(OLECHAR**)elementStorageAddress )) == NULL) {
SafeArrayUnlock(psa);
return E_OUTOFMEMORY;
} else
*((BSTR*)pv) = pbstrReturnedStr;
} else if( isPointer(psa->fFeatures) ) /* simply copy the pointer */
pv = *((PVOID*)elementStorageAddress);
else /* copy the bytes */
memcpy(pv, elementStorageAddress, SafeArrayGetElemsize(psa) );
} else {
ERR(ole, "SafeArray: Cannot lock array....\n");
return E_UNEXPECTED; /* UNDOC error condition */
}
return( SafeArrayUnlock(psa) );
}
/*************************************************************************
* return the UP bound for a given array dimension
*/
HRESULT WINAPI SafeArrayGetUBound(
SAFEARRAY *psa,
UINT nDim,
LONG *plUbound)
{
if(! validArg(psa))
return E_INVALIDARG;
if(nDim > psa->cDims)
return DISP_E_BADINDEX;
*plUbound = psa->rgsabound[nDim-1].lLbound +
psa->rgsabound[nDim-1].cElements - 1;
return S_OK;
}
/*************************************************************************
* Return the LO bound for a given array dimension
*/
HRESULT WINAPI SafeArrayGetLBound(
SAFEARRAY *psa,
UINT nDim,
LONG *plLbound)
{
if(! validArg(psa))
return E_INVALIDARG;
if(nDim > psa->cDims)
return DISP_E_BADINDEX;
*plLbound = psa->rgsabound[nDim-1].lLbound;
return S_OK;
}
/*************************************************************************
* returns the number of dimension in the array
*/
UINT WINAPI SafeArrayGetDim(
SAFEARRAY * psa)
{
/*
* A quick test in Windows shows that the behavior here for an invalid
* pointer is to return 0.
*/
if(! validArg(psa))
return 0;
return psa->cDims;
}
/*************************************************************************
* Return the size of the element in the array
*/
UINT WINAPI SafeArrayGetElemsize(
SAFEARRAY * psa)
{
/*
* A quick test in Windows shows that the behavior here for an invalid
* pointer is to return 0.
*/
if(! validArg(psa))
return 0;
return psa->cbElements;
}
/*************************************************************************
* increment the access count and return the data
*/
HRESULT WINAPI SafeArrayAccessData(
SAFEARRAY *psa,
void **ppvData)
{
HRESULT hRes;
if(! validArg(psa))
return E_INVALIDARG;
hRes = SafeArrayLock(psa);
switch (hRes) {
case S_OK:
(*ppvData) = psa->pvData;
break;
case E_INVALIDARG:
(*ppvData) = NULL;
return E_INVALIDARG;
}
return S_OK;
}
/*************************************************************************
* Decrement the access count
*/
HRESULT WINAPI SafeArrayUnaccessData(
SAFEARRAY * psa)
{
if(! validArg(psa))
return E_INVALIDARG;
return(SafeArrayUnlock(psa));
}
/************************************************************************
* Return a pointer to the element at rgIndices
*/
HRESULT WINAPI SafeArrayPtrOfIndex(
SAFEARRAY *psa,
LONG *rgIndices,
void **ppvData)
{
ULONG stepCountInSAData = 0; /* Number of array item to skip to get to
the desired one... */
if(! validArg(psa))
return E_INVALIDARG;
if(! validCoordinate(rgIndices, psa))
return DISP_E_BADINDEX;
/* Figure out the number of items to skip */
stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
*ppvData = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
return S_OK;
}
/************************************************************************
* Frees the memory data bloc
*/
HRESULT WINAPI SafeArrayDestroyData(
SAFEARRAY *psa)
{
HRESULT hRes;
ULONG ulWholeArraySize; /* count spot in array */
ULONG ulDataIter; /* to iterate the data space */
IUnknown *punk;
BSTR bstr;
if(! validArg(psa))
return E_INVALIDARG;
if(psa->cLocks > 0)
return DISP_E_ARRAYISLOCKED;
ulWholeArraySize = getArraySize(psa);
if(isPointer(psa->fFeatures)) { /* release the pointers */
for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
punk = *(IUnknown**)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));
if( punk != NULL)
IUnknown_Release(punk);
}
} else if(psa->fFeatures & FADF_BSTR) { /* deallocate the obj */
for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
bstr = *(BSTR*)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));
if( bstr != NULL)
SysFreeString( bstr );
}
}
/* check if this array is a Vector, in which case do not free the data
block since it has been allocated by AllocDescriptor and therefore
deserve to be freed by DestroyDescriptor */
if(!(psa->fFeatures & FADF_FIXEDSIZE)) { /* Set when we do CreateVector */
/* free the whole chunk */
if((hRes = HeapFree( GetProcessHeap(), 0, psa->pvData)) == 0) /*falied*/
return E_UNEXPECTED; /* UNDOC error condition */
psa->pvData = NULL;
}
return S_OK;
}
/************************************************************************
* Copy the psaSource's data block into psaTarget if dimension and size
* permits it.
*/
HRESULT WINAPI SafeArrayCopyData(
SAFEARRAY *psaSource,
SAFEARRAY **psaTarget)
{
USHORT cDimCount; /* looper */
LONG lDelta; /* looper */
IUnknown *punk;
ULONG ulWholeArraySize; /* Number of item in SA */
BSTR bstr;
if(! (validArg(psaSource) && validArg(*psaTarget)) )
return E_INVALIDARG;
if(SafeArrayGetDim(psaSource) != SafeArrayGetDim(*psaTarget))
return E_INVALIDARG;
ulWholeArraySize = getArraySize(psaSource);
/* The two arrays boundaries must be of same lenght */
for(cDimCount=0;cDimCount < psaSource->cDims; cDimCount++)
if( psaSource->rgsabound[cDimCount].cElements !=
(*psaTarget)->rgsabound[cDimCount].cElements)
return E_INVALIDARG;
if( isPointer((*psaTarget)->fFeatures) ) { /* the target contains ptr
that must be released */
for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
punk = *(IUnknown**)
((char *) (*psaTarget)->pvData + (lDelta * (*psaTarget)->cbElements));
if( punk != NULL)
IUnknown_Release(punk);
}
} else if( (*psaTarget)->fFeatures & FADF_BSTR) { /* the target contain BSTR
that must be freed */
for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
bstr =
*(BSTR*)((char *) (*psaTarget)->pvData + (lDelta * (*psaTarget)->cbElements));
if( bstr != NULL)
SysFreeString( bstr );
}
}
return duplicateData(psaSource, psaTarget);
}
/************************************************************************
* Deallocates all memory reserved for the SafeArray
*/
HRESULT WINAPI SafeArrayDestroy(
SAFEARRAY * psa)
{
HRESULT hRes;
if(! validArg(psa))
return E_INVALIDARG;
if(psa->cLocks > 0)
return DISP_E_ARRAYISLOCKED;
if((hRes = SafeArrayDestroyData( psa )) == S_OK)
if((hRes = SafeArrayDestroyDescriptor( psa )) == S_OK)
return S_OK;
return E_UNEXPECTED; /* UNDOC error condition */
}
/************************************************************************
* Make a dupplicate of a SafeArray
*/
HRESULT WINAPI SafeArrayCopy(
SAFEARRAY *psa,
SAFEARRAY **ppsaOut)
{
HRESULT hRes;
DWORD dAllocSize;
if(! validArg(psa))
return E_INVALIDARG;
if((hRes=SafeArrayAllocDescriptor(psa->cDims, ppsaOut)) == S_OK){
/* Duplicate the SAFEARRAY struc */
memcpy(*ppsaOut, psa,
sizeof(*psa)+(sizeof(*(psa->rgsabound))*(psa->cDims-1)));
(*ppsaOut)->pvData = NULL; /* do not point to the same data area */
/* Get the allocated memory size for source and allocate it for target */
dAllocSize = HeapSize(GetProcessHeap(), 0, psa->pvData);
(*ppsaOut)->pvData =
HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dAllocSize);
if( (*ppsaOut)->pvData != NULL) { /* HeapAlloc succeed */
if( (hRes=duplicateData(psa, ppsaOut)) != S_OK) { /* E_OUTOFMEMORY */
HeapFree(GetProcessHeap(), 0, (*ppsaOut)->pvData);
(*ppsaOut)->pvData = NULL;
SafeArrayDestroyDescriptor(*ppsaOut);
return hRes;
}
} else { /* failed to allocate or dupplicate... */
SafeArrayDestroyDescriptor(*ppsaOut);
return E_UNEXPECTED; /* UNDOC error condition */
}
} else { /* failed to allocate mem for descriptor */
return E_OUTOFMEMORY; /* UNDOC error condiftion */
}
return S_OK;
}
/************************************************************************
* Creates a one dimension safearray where the data is next to the
* SAFEARRAY structure.
*/
SAFEARRAY* WINAPI SafeArrayCreateVector(
VARTYPE vt,
LONG lLbound,
ULONG cElements)
{
SAFEARRAY *psa;
/* Validate supported VARTYPE */
if ( (vt >= LAST_VARTYPE) ||
( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
return NULL;
/* Allocate memory for the array descriptor and data contiguously */
if( FAILED( psa = HeapAlloc( GetProcessHeap(),
HEAP_ZERO_MEMORY,
(sizeof(*psa) + (VARTYPE_SIZE[vt] * cElements))))) {
return NULL;
}
/* setup data members... */
psa->cDims = 1; /* always and forever */
psa->fFeatures = getFeatures(vt) | FADF_FIXEDSIZE;
psa->cLocks = 0;
psa->pvData = psa+sizeof(*psa);
psa->cbElements = VARTYPE_SIZE[vt];
psa->rgsabound[0].cElements = cElements;
psa->rgsabound[0].lLbound = lLbound;
return(psa);
}
/************************************************************************
* Changes the caracteristics of the last dimension of the SafeArray
*/
HRESULT WINAPI SafeArrayRedim(
SAFEARRAY *psa,
SAFEARRAYBOUND *psaboundNew)
{
LONG lDelta; /* hold difference in size */
USHORT cDims=1; /* dims counter */
if( !validArg(psa) )
return E_INVALIDARG;
if( psa->cLocks > 0 )
return DISP_E_ARRAYISLOCKED;
if( psa->fFeatures & FADF_FIXEDSIZE )
return E_INVALIDARG;
if( SafeArrayLock(psa)==E_UNEXPECTED )
return E_UNEXPECTED;/* UNDOC error condition */
/* find the delta in number of array spot to apply to the new array */
lDelta = psaboundNew->cElements - psa->rgsabound[0].cElements;
for(; cDims < psa->cDims; cDims++)
/* delta in number of spot implied by modifying the last dimension */
lDelta *= psa->rgsabound[cDims].cElements;
if (lDelta == 0) { ;/* same size, maybe a change of lLbound, just set it */
} else /* need to enlarge (lDelta +) reduce (lDelta -) */
if(! resizeSafeArray(psa, lDelta))
return E_UNEXPECTED; /* UNDOC error condition */
/* the only modifyable dimension sits in [0] as the dimensions were reversed
at array creation time... */
psa->rgsabound[0].cElements = psaboundNew->cElements;
psa->rgsabound[0].lLbound = psaboundNew->lLbound;
return SafeArrayUnlock(psa);
}
/************************************************************************
* NOT WINDOWS API - SafeArray* Utility functions
************************************************************************/
/************************************************************************
* Used to validate the SAFEARRAY type of arg
*/
static BOOL validArg(
SAFEARRAY *psa)
{
SAFEARRAYBOUND *sab;
LONG psaSize = 0;
LONG descSize = 0;
LONG fullSize = 0;
/*
* Let's check for the null pointer just in case.
*/
if (psa == NULL)
return FALSE;
/* Check whether the size of the chunk make sens... That's the only thing
I can think of now... */
psaSize = HeapSize(GetProcessHeap(), 0, psa);
/* size of the descriptor when the SA is not created with CreateVector */
descSize = sizeof(*psa) + (sizeof(*sab) * (psa->cDims-1));
/* size of the descriptor + data when created with CreateVector */
fullSize = sizeof(*psa) + (psa->cbElements * psa->rgsabound[0].cElements);
return((psaSize >= descSize) || (psaSize >= fullSize));
}
/************************************************************************
* Used to reallocate memory
*/
static BOOL resizeSafeArray(
SAFEARRAY *psa,
LONG lDelta)
{
ULONG ulWholeArraySize; /* use as multiplicator */
PVOID pvNewBlock = NULL;
IUnknown *punk;
BSTR bstr;
ulWholeArraySize = getArraySize(psa);
if(lDelta < 0) { /* array needs to be shorthen */
if( isPointer(psa->fFeatures)) /* ptr that need to be released */
for(;lDelta < 0; lDelta++) {
punk = *(IUnknown**)
((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
if( punk != NULL )
IUnknown_Release(punk);
}
else if(psa->fFeatures & FADF_BSTR) /* BSTR that need to be freed */
for(;lDelta < 0; lDelta++) {
bstr = *(BSTR*)
((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
if( bstr != NULL )
SysFreeString( bstr );
}
}
/* Ok now, if we are enlarging the array, we *MUST* move the whole block
pointed to by pvData. If we are shorthening the array, this move is
optional but we do it anyway becuase the benefit is that we are
releasing to the system the unused memory */
if((pvNewBlock = HeapReAlloc(GetProcessHeap(), 0, psa->pvData,
(ulWholeArraySize + lDelta) * psa->cbElements)) == NULL)
return FALSE; /* TODO If we get here it means:
SHRINK situation : we've deleted the undesired
data and did not release the memory
GROWING situation: we've been unable to grow the array
*/
/* reassign to the new block of data */
psa->pvData = pvNewBlock;
return TRUE;
}
/************************************************************************
* Used to set the fFeatures data member of the SAFEARRAY structure.
*/
static INT getFeatures(
VARTYPE vt)
{
switch(vt) {
case VT_UNKNOWN: return FADF_UNKNOWN;
case VT_DISPATCH: return FADF_DISPATCH;
case VT_BSTR: return FADF_BSTR;
}
return 0;
}
/************************************************************************
* Used to figure out if the fFeatures data member of the SAFEARRAY
* structure contain any information about the type of data stored...
*/
static BOOL isPointer(
USHORT feature)
{
switch(feature) {
case FADF_UNKNOWN: return TRUE; /* those are pointers */
case FADF_DISPATCH: return TRUE;
}
return FALSE;
}
/************************************************************************
* Used to calculate the displacement when accessing or modifying
* safearray data set.
*
* Parameters: - LONG *coor is the desired location in the multidimension
* table. Ex for a 3 dim table: coor[] = {1,2,3};
* - ULONG *mat is the format of the table. Ex for a 3 dim
* table mat[] = {4,4,4};
* - USHORT dim is the number of dimension of the SafeArray
*/
static ULONG calcDisplacement(
LONG *coor,
SAFEARRAYBOUND *mat,
LONG dim)
{
ULONG res = 0;
LONG iterDim;
for(iterDim=0; iterDim<dim; iterDim++)
/* the -mat[dim] bring coor[dim] relative to 0 for calculation */
res += ((coor[iterDim]-mat[iterDim].lLbound) *
endOfDim(coor, mat, iterDim+1, dim));
TRACE(ole, "SafeArray: calculated displacement is %lu.\n", res);
return(res);
}
/************************************************************************
* Recursivity agent for calcDisplacement method. Used within Put and
* Get methods.
*/
static INT endOfDim(
LONG *coor,
SAFEARRAYBOUND *mat,
LONG dim,
LONG realDim)
{
if(dim==realDim)
return 1;
else
return (endOfDim(coor, mat, dim+1, realDim) * mat[dim].cElements);
}
/************************************************************************
* Method used to validate the coordinate received in Put and Get
* methods.
*/
static BOOL validCoordinate(
LONG *coor,
SAFEARRAY *psa)
{
INT iter=0;
LONG lUBound;
LONG lLBound;
HRESULT hRes;
for(; iter<psa->cDims; iter++) {
if((hRes = SafeArrayGetLBound(psa, iter, &lLBound)) != S_OK)
return FALSE;
if((hRes = SafeArrayGetUBound(psa, iter, &lUBound)) != S_OK)
return FALSE;
if(lLBound == lUBound)
return FALSE;
if((coor[iter] >= lLBound) && (coor[iter] <= lUBound))
return TRUE;
else
return FALSE;
}
return FALSE;
}
/************************************************************************
* Method used to calculate the number of cells of the SA
*/
static ULONG getArraySize(
SAFEARRAY *psa)
{
USHORT cCount;
ULONG ulWholeArraySize = 1;
for(cCount=0; cCount < psa->cDims; cCount++) /* foreach dimensions... */
ulWholeArraySize *= psa->rgsabound[cCount].cElements;
return ulWholeArraySize;
}
/************************************************************************
* Method used to handle data space dupplication for Copy32 and CopyData32
*/
static HRESULT duplicateData(
SAFEARRAY *psa,
SAFEARRAY **ppsaOut)
{
ULONG ulWholeArraySize; /* size of the thing */
LONG lDelta;
IUnknown *punk;
BSTR pbstrReAllocStr = NULL; /* BSTR reallocated */
ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
SafeArrayLock(*ppsaOut);
if( isPointer(psa->fFeatures) ) { /* If datatype is object increment
object's reference count */
for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
punk = *(IUnknown**)((char *) psa->pvData+(lDelta * psa->cbElements));
if( punk != NULL)
IUnknown_AddRef(punk);
}
/* Copy the source array data into target array */
memcpy((*ppsaOut)->pvData, psa->pvData,
ulWholeArraySize*psa->cbElements);
} else if( psa->fFeatures & FADF_BSTR ) { /* if datatype is BSTR allocate
the BSTR in the new array */
for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
if(( pbstrReAllocStr = SysAllocString(
*(BSTR*)((char *) psa->pvData+(lDelta * psa->cbElements)))) == NULL) {
SafeArrayUnlock(*ppsaOut);
return E_OUTOFMEMORY;
}
*((BSTR*)((char *) (*ppsaOut)->pvData+(lDelta * psa->cbElements))) =
pbstrReAllocStr;
}
} else { /* Simply copy the source array data into target array */
memcpy((*ppsaOut)->pvData, psa->pvData,
ulWholeArraySize*psa->cbElements);
}
SafeArrayUnlock(*ppsaOut);
return S_OK;
}