|  | /************************************************************************* | 
|  | * OLE Automation - SafeArray | 
|  | * | 
|  | * This file contains the implementation of the SafeArray functions. | 
|  | * | 
|  | * Copyright 1999 Sylvain St-Germain | 
|  | * Copyright 2002-2003 Marcus Meissner | 
|  | * Copyright 2003 Jon Griffiths | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  | /* Memory Layout of a SafeArray: | 
|  | * | 
|  | * -0x10: start of memory. | 
|  | * -0x10: GUID for VT_DISPATCH and VT_UNKNOWN safearrays (if FADF_HAVEIID) | 
|  | * -0x04: DWORD varianttype; (for all others, except VT_RECORD) (if FADF_HAVEVARTYPE) | 
|  | *  -0x4: IRecordInfo* iface;  (if FADF_RECORD, for VT_RECORD (can be NULL)) | 
|  | *  0x00: SAFEARRAY, | 
|  | *  0x10: SAFEARRAYBOUNDS[0...] | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  |  | 
|  | #include <string.h> | 
|  | #include <stdarg.h> | 
|  | #include <stdio.h> | 
|  |  | 
|  | #define COBJMACROS | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winerror.h" | 
|  | #include "winbase.h" | 
|  | #include "variant.h" | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(variant); | 
|  |  | 
|  | /************************************************************************ | 
|  | * SafeArray {OLEAUT32} | 
|  | * | 
|  | * NOTES | 
|  | * The SafeArray data type provides the underlying interface for Ole | 
|  | * Automations arrays, used for example to represent array types in | 
|  | * Visual Basic(tm) and to gather user defined parameters for invocation through | 
|  | * an IDispatch interface. | 
|  | * | 
|  | * Safe arrays provide bounds checking and automatically manage the data | 
|  | * types they contain, for example handing reference counting and copying | 
|  | * of interface pointers. User defined types can be stored in arrays | 
|  | * using the IRecordInfo interface. | 
|  | * | 
|  | * There are two types of SafeArray, normal and vectors. Normal arrays can have | 
|  | * multiple dimensions and the data for the array is allocated separately from | 
|  | * the array header. This is the most flexible type of array. Vectors, on the | 
|  | * other hand, are fixed in size and consist of a single allocated block, and a | 
|  | * single dimension. | 
|  | * | 
|  | * DATATYPES | 
|  | * The following types of data can be stored within a SafeArray. | 
|  | * Numeric: | 
|  | *|  VT_I1, VT_UI1, VT_I2, VT_UI2, VT_I4, VT_UI4, VT_I8, VT_UI8, VT_INT, VT_UINT, | 
|  | *|  VT_R4, VT_R8, VT_CY, VT_DECIMAL | 
|  | * Interfaces: | 
|  | *|  VT_DISPATCH, VT_UNKNOWN, VT_RECORD | 
|  | * Other: | 
|  | *|  VT_VARIANT, VT_INT_PTR, VT_UINT_PTR, VT_BOOL, VT_ERROR, VT_DATE, VT_BSTR | 
|  | * | 
|  | * FUNCTIONS | 
|  | *  BstrFromVector() | 
|  | *  VectorFromBstr() | 
|  | */ | 
|  |  | 
|  | /* Undocumented hidden space before the start of a SafeArray descriptor */ | 
|  | #define SAFEARRAY_HIDDEN_SIZE sizeof(GUID) | 
|  |  | 
|  | /* Allocate memory */ | 
|  | static inline LPVOID SAFEARRAY_Malloc(ULONG ulSize) | 
|  | { | 
|  | /* FIXME: Memory should be allocated and freed using a per-thread IMalloc | 
|  | *        instance returned from CoGetMalloc(). | 
|  | */ | 
|  | return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ulSize); | 
|  | } | 
|  |  | 
|  | /* Free memory */ | 
|  | static inline BOOL SAFEARRAY_Free(LPVOID lpData) | 
|  | { | 
|  | return HeapFree(GetProcessHeap(), 0, lpData); | 
|  | } | 
|  |  | 
|  | /* Get the size of a supported VT type (0 means unsupported) */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* Set the hidden data for an array */ | 
|  | static inline void SAFEARRAY_SetHiddenDWORD(SAFEARRAY* psa, DWORD dw) | 
|  | { | 
|  | /* Implementation data is stored in the 4 bytes before the header */ | 
|  | LPDWORD lpDw = (LPDWORD)psa; | 
|  | lpDw[-1] = dw; | 
|  | } | 
|  |  | 
|  | /* Get the hidden data from an array */ | 
|  | static inline DWORD SAFEARRAY_GetHiddenDWORD(SAFEARRAY* psa) | 
|  | { | 
|  | LPDWORD lpDw = (LPDWORD)psa; | 
|  | return lpDw[-1]; | 
|  | } | 
|  |  | 
|  | /* Get the number of cells in a SafeArray */ | 
|  | static ULONG SAFEARRAY_GetCellCount(const SAFEARRAY *psa) | 
|  | { | 
|  | const SAFEARRAYBOUND* psab = psa->rgsabound; | 
|  | USHORT cCount = psa->cDims; | 
|  | ULONG ulNumCells = 1; | 
|  |  | 
|  | while (cCount--) | 
|  | { | 
|  | /* This is a valid bordercase. See testcases. -Marcus */ | 
|  | if (!psab->cElements) | 
|  | return 0; | 
|  | ulNumCells *= psab->cElements; | 
|  | psab++; | 
|  | } | 
|  | return ulNumCells; | 
|  | } | 
|  |  | 
|  | /* Allocate a descriptor for an array */ | 
|  | static HRESULT SAFEARRAY_AllocDescriptor(ULONG ulSize, SAFEARRAY **ppsaOut) | 
|  | { | 
|  | *ppsaOut = (SAFEARRAY*)((char*)SAFEARRAY_Malloc(ulSize + SAFEARRAY_HIDDEN_SIZE) + SAFEARRAY_HIDDEN_SIZE); | 
|  |  | 
|  | if (!*ppsaOut) | 
|  | return E_UNEXPECTED; | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /* Set the features of an array */ | 
|  | static void SAFEARRAY_SetFeatures(VARTYPE vt, SAFEARRAY *psa) | 
|  | { | 
|  | /* Set the IID if we have one, otherwise set the type */ | 
|  | if (vt == VT_DISPATCH) | 
|  | { | 
|  | psa->fFeatures = FADF_HAVEIID; | 
|  | SafeArraySetIID(psa, &IID_IDispatch); | 
|  | } | 
|  | else if (vt == VT_UNKNOWN) | 
|  | { | 
|  | psa->fFeatures = FADF_HAVEIID; | 
|  | SafeArraySetIID(psa, &IID_IUnknown); | 
|  | } | 
|  | else if (vt == VT_RECORD) | 
|  | psa->fFeatures = FADF_RECORD; | 
|  | else | 
|  | { | 
|  | psa->fFeatures = FADF_HAVEVARTYPE; | 
|  | SAFEARRAY_SetHiddenDWORD(psa, vt); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Create an array */ | 
|  | static SAFEARRAY* SAFEARRAY_Create(VARTYPE vt, UINT cDims, const SAFEARRAYBOUND *rgsabound, ULONG ulSize) | 
|  | { | 
|  | SAFEARRAY *psa = NULL; | 
|  | unsigned int i; | 
|  |  | 
|  | if (!rgsabound) | 
|  | return NULL; | 
|  |  | 
|  | if (SUCCEEDED(SafeArrayAllocDescriptorEx(vt, cDims, &psa))) | 
|  | { | 
|  | 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; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < cDims; i++) | 
|  | memcpy(psa->rgsabound + i, rgsabound + cDims - 1 - i, sizeof(SAFEARRAYBOUND)); | 
|  |  | 
|  | if (ulSize) | 
|  | psa->cbElements = ulSize; | 
|  |  | 
|  | if (!psa->cbElements || FAILED(SafeArrayAllocData(psa))) | 
|  | { | 
|  | SafeArrayDestroyDescriptor(psa); | 
|  | psa = NULL; | 
|  | } | 
|  | } | 
|  | return psa; | 
|  | } | 
|  |  | 
|  | /* Create an array as a vector */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* Free data items in an array */ | 
|  | static HRESULT SAFEARRAY_DestroyData(SAFEARRAY *psa, ULONG ulStartCell) | 
|  | { | 
|  | if (psa->pvData && !(psa->fFeatures & FADF_DATADELETED)) | 
|  | { | 
|  | ULONG ulCellCount = SAFEARRAY_GetCellCount(psa); | 
|  |  | 
|  | if (ulStartCell > ulCellCount) { | 
|  | FIXME("unexpted ulcellcount %d, start %d\n",ulCellCount,ulStartCell); | 
|  | return E_UNEXPECTED; | 
|  | } | 
|  |  | 
|  | ulCellCount -= ulStartCell; | 
|  |  | 
|  | if (psa->fFeatures & (FADF_UNKNOWN|FADF_DISPATCH)) | 
|  | { | 
|  | LPUNKNOWN *lpUnknown = (LPUNKNOWN *)psa->pvData + ulStartCell; | 
|  |  | 
|  | while(ulCellCount--) | 
|  | { | 
|  | if (*lpUnknown) | 
|  | IUnknown_Release(*lpUnknown); | 
|  | lpUnknown++; | 
|  | } | 
|  | } | 
|  | else if (psa->fFeatures & (FADF_RECORD)) | 
|  | { | 
|  | IRecordInfo *lpRecInfo; | 
|  |  | 
|  | if (SUCCEEDED(SafeArrayGetRecordInfo(psa, &lpRecInfo))) | 
|  | { | 
|  | PBYTE pRecordData = psa->pvData; | 
|  | while(ulCellCount--) | 
|  | { | 
|  | IRecordInfo_RecordClear(lpRecInfo, pRecordData); | 
|  | pRecordData += psa->cbElements; | 
|  | } | 
|  | IRecordInfo_Release(lpRecInfo); | 
|  | } | 
|  | } | 
|  | else if (psa->fFeatures & FADF_BSTR) | 
|  | { | 
|  | BSTR* lpBstr = (BSTR*)psa->pvData + ulStartCell; | 
|  |  | 
|  | while(ulCellCount--) | 
|  | { | 
|  | SysFreeString(*lpBstr); | 
|  | lpBstr++; | 
|  | } | 
|  | } | 
|  | else if (psa->fFeatures & FADF_VARIANT) | 
|  | { | 
|  | VARIANT* lpVariant = (VARIANT*)psa->pvData + ulStartCell; | 
|  |  | 
|  | while(ulCellCount--) | 
|  | { | 
|  | HRESULT hRet = VariantClear(lpVariant); | 
|  |  | 
|  | if (FAILED(hRet)) FIXME("VariantClear of element failed!\n"); | 
|  | lpVariant++; | 
|  | } | 
|  | } | 
|  | } | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /* Copy data items from one array to another */ | 
|  | static HRESULT SAFEARRAY_CopyData(SAFEARRAY *psa, SAFEARRAY *dest) | 
|  | { | 
|  | if (!psa->pvData) | 
|  | return S_OK; | 
|  | else if (!dest->pvData || psa->fFeatures & FADF_DATADELETED) | 
|  | return E_INVALIDARG; | 
|  | else | 
|  | { | 
|  | ULONG ulCellCount = SAFEARRAY_GetCellCount(psa); | 
|  |  | 
|  | dest->fFeatures = (dest->fFeatures & FADF_CREATEVECTOR) | | 
|  | (psa->fFeatures & ~(FADF_CREATEVECTOR|FADF_DATADELETED)); | 
|  |  | 
|  | if (psa->fFeatures & FADF_VARIANT) | 
|  | { | 
|  | VARIANT* lpVariant = psa->pvData; | 
|  | VARIANT* lpDest = dest->pvData; | 
|  |  | 
|  | while(ulCellCount--) | 
|  | { | 
|  | HRESULT hRet; | 
|  |  | 
|  | hRet = VariantCopy(lpDest, lpVariant); | 
|  | if (FAILED(hRet)) FIXME("VariantCopy failed with 0x%x\n", hRet); | 
|  | lpVariant++; | 
|  | lpDest++; | 
|  | } | 
|  | } | 
|  | else if (psa->fFeatures & FADF_BSTR) | 
|  | { | 
|  | BSTR* lpBstr = psa->pvData; | 
|  | BSTR* lpDest = dest->pvData; | 
|  |  | 
|  | while(ulCellCount--) | 
|  | { | 
|  | if (*lpBstr) | 
|  | { | 
|  | *lpDest = SysAllocStringByteLen((char*)*lpBstr, SysStringByteLen(*lpBstr)); | 
|  | if (!*lpDest) | 
|  | return E_OUTOFMEMORY; | 
|  | } | 
|  | else | 
|  | *lpDest = NULL; | 
|  | lpBstr++; | 
|  | lpDest++; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Copy the data over */ | 
|  | memcpy(dest->pvData, psa->pvData, ulCellCount * psa->cbElements); | 
|  |  | 
|  | if (psa->fFeatures & (FADF_UNKNOWN|FADF_DISPATCH)) | 
|  | { | 
|  | LPUNKNOWN *lpUnknown = dest->pvData; | 
|  |  | 
|  | while(ulCellCount--) | 
|  | { | 
|  | if (*lpUnknown) | 
|  | IUnknown_AddRef(*lpUnknown); | 
|  | lpUnknown++; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (psa->fFeatures & FADF_RECORD) | 
|  | { | 
|  | IRecordInfo* pRecInfo = NULL; | 
|  |  | 
|  | SafeArrayGetRecordInfo(psa, &pRecInfo); | 
|  | SafeArraySetRecordInfo(dest, pRecInfo); | 
|  |  | 
|  | if (pRecInfo) | 
|  | { | 
|  | /* Release because Get() adds a reference */ | 
|  | IRecordInfo_Release(pRecInfo); | 
|  | } | 
|  | } | 
|  | else if (psa->fFeatures & FADF_HAVEIID) | 
|  | { | 
|  | GUID guid; | 
|  | SafeArrayGetIID(psa, &guid); | 
|  | SafeArraySetIID(dest, &guid); | 
|  | } | 
|  | else if (psa->fFeatures & FADF_HAVEVARTYPE) | 
|  | { | 
|  | SAFEARRAY_SetHiddenDWORD(dest, SAFEARRAY_GetHiddenDWORD(psa)); | 
|  | } | 
|  | } | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayAllocDescriptor (OLEAUT32.36) | 
|  | * | 
|  | * Allocate and initialise a descriptor for a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  cDims   [I] Number of dimensions of the array | 
|  | *  ppsaOut [O] Destination for new descriptor | 
|  | * | 
|  | * RETURNS | 
|  | * Success: S_OK. ppsaOut is filled with a newly allocated descriptor. | 
|  | * Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayAllocDescriptor(UINT cDims, SAFEARRAY **ppsaOut) | 
|  | { | 
|  | LONG allocSize; | 
|  |  | 
|  | TRACE("(%d,%p)\n", cDims, ppsaOut); | 
|  |  | 
|  | if (!cDims || cDims >= 0x10000) /* Maximum 65535 dimensions */ | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | if (!ppsaOut) | 
|  | return E_POINTER; | 
|  |  | 
|  | /* We need enough space for the header and its bounds */ | 
|  | allocSize = sizeof(SAFEARRAY) + sizeof(SAFEARRAYBOUND) * (cDims - 1); | 
|  |  | 
|  | if (FAILED(SAFEARRAY_AllocDescriptor(allocSize, ppsaOut))) | 
|  | return E_UNEXPECTED; | 
|  |  | 
|  | (*ppsaOut)->cDims = cDims; | 
|  |  | 
|  | TRACE("(%d): %u bytes allocated for descriptor.\n", cDims, allocSize); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayAllocDescriptorEx (OLEAUT32.41) | 
|  | * | 
|  | * Allocate and initialise a descriptor for a SafeArray of a given type. | 
|  | * | 
|  | * PARAMS | 
|  | *  vt      [I] The type of items to store in the array | 
|  | *  cDims   [I] Number of dimensions of the array | 
|  | *  ppsaOut [O] Destination for new descriptor | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. ppsaOut is filled with a newly allocated descriptor. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | *  - This function does not check that vt is an allowed VARTYPE. | 
|  | *  - Unlike SafeArrayAllocDescriptor(), vt is associated with the array. | 
|  | *  See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayAllocDescriptorEx(VARTYPE vt, UINT cDims, SAFEARRAY **ppsaOut) | 
|  | { | 
|  | ULONG cbElements; | 
|  | HRESULT hRet = E_UNEXPECTED; | 
|  |  | 
|  | TRACE("(%d->%s,%d,%p)\n", vt, debugstr_vt(vt), cDims, ppsaOut); | 
|  |  | 
|  | cbElements = SAFEARRAY_GetVTSize(vt); | 
|  | if (!cbElements) | 
|  | WARN("Creating a descriptor with an invalid VARTYPE!\n"); | 
|  |  | 
|  | hRet = SafeArrayAllocDescriptor(cDims, ppsaOut); | 
|  |  | 
|  | if (SUCCEEDED(hRet)) | 
|  | { | 
|  | SAFEARRAY_SetFeatures(vt, *ppsaOut); | 
|  | (*ppsaOut)->cbElements = cbElements; | 
|  | } | 
|  | return hRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayAllocData (OLEAUT32.37) | 
|  | * | 
|  | * Allocate the data area of a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa [I] SafeArray to allocate the data area of. | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. The data area is allocated and initialised. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | *  See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayAllocData(SAFEARRAY *psa) | 
|  | { | 
|  | HRESULT hRet = E_INVALIDARG; | 
|  |  | 
|  | TRACE("(%p)\n", psa); | 
|  |  | 
|  | if (psa) | 
|  | { | 
|  | ULONG ulSize = SAFEARRAY_GetCellCount(psa); | 
|  |  | 
|  | psa->pvData = SAFEARRAY_Malloc(ulSize * psa->cbElements); | 
|  |  | 
|  | if (psa->pvData) | 
|  | { | 
|  | hRet = S_OK; | 
|  | TRACE("%u bytes allocated for data at %p (%u objects).\n", | 
|  | ulSize * psa->cbElements, psa->pvData, ulSize); | 
|  | } | 
|  | else | 
|  | hRet = E_OUTOFMEMORY; | 
|  | } | 
|  | return hRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayCreate (OLEAUT32.15) | 
|  | * | 
|  | * Create a new SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  vt        [I] Type to store in the safe array | 
|  | *  cDims     [I] Number of array dimensions | 
|  | *  rgsabound [I] Bounds of the array dimensions | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: A pointer to a new array object. | 
|  | *  Failure: NULL, if any parameter is invalid or memory allocation fails. | 
|  | * | 
|  | * NOTES | 
|  | *  Win32 allows arrays with 0 sized dimensions. This bug is not reproduced | 
|  | *  in the Wine implementation. | 
|  | *  See SafeArray. | 
|  | */ | 
|  | SAFEARRAY* WINAPI SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND *rgsabound) | 
|  | { | 
|  | TRACE("(%d->%s,%d,%p)\n", vt, debugstr_vt(vt), cDims, rgsabound); | 
|  |  | 
|  | if (vt == VT_RECORD) | 
|  | return NULL; | 
|  |  | 
|  | return SAFEARRAY_Create(vt, cDims, rgsabound, 0); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayCreateEx (OLEAUT32.15) | 
|  | * | 
|  | * Create a new SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  vt        [I] Type to store in the safe array | 
|  | *  cDims     [I] Number of array dimensions | 
|  | *  rgsabound [I] Bounds of the array dimensions | 
|  | *  pvExtra   [I] Extra data | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: A pointer to a new array object. | 
|  | *  Failure: NULL, if any parameter is invalid or memory allocation fails. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | SAFEARRAY* WINAPI SafeArrayCreateEx(VARTYPE vt, UINT cDims, SAFEARRAYBOUND *rgsabound, LPVOID pvExtra) | 
|  | { | 
|  | ULONG ulSize = 0; | 
|  | IRecordInfo* iRecInfo = pvExtra; | 
|  | SAFEARRAY* psa; | 
|  |  | 
|  | TRACE("(%d->%s,%d,%p,%p)\n", vt, debugstr_vt(vt), cDims, rgsabound, pvExtra); | 
|  |  | 
|  | if (vt == VT_RECORD) | 
|  | { | 
|  | if  (!iRecInfo) | 
|  | return NULL; | 
|  | IRecordInfo_GetSize(iRecInfo, &ulSize); | 
|  | } | 
|  | psa = SAFEARRAY_Create(vt, cDims, rgsabound, ulSize); | 
|  |  | 
|  | if (pvExtra) | 
|  | { | 
|  | switch(vt) | 
|  | { | 
|  | case VT_RECORD: | 
|  | SafeArraySetRecordInfo(psa, pvExtra); | 
|  | break; | 
|  | case VT_UNKNOWN: | 
|  | case VT_DISPATCH: | 
|  | SafeArraySetIID(psa, pvExtra); | 
|  | break; | 
|  | } | 
|  | } | 
|  | return psa; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		SafeArrayCreateVector (OLEAUT32.411) | 
|  | * | 
|  | * Create a one dimensional, contiguous SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  vt        [I] Type to store in the safe array | 
|  | *  lLbound   [I] Lower bound of the array | 
|  | *  cElements [I] Number of elements in the array | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: A pointer to a new array object. | 
|  | *  Failure: NULL, if any parameter is invalid or memory allocation fails. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | 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)); | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		SafeArrayCreateVectorEx (OLEAUT32.411) | 
|  | * | 
|  | * Create a one dimensional, contiguous SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  vt        [I] Type to store in the safe array | 
|  | *  lLbound   [I] Lower bound of the array | 
|  | *  cElements [I] Number of elements in the array | 
|  | *  pvExtra   [I] Extra data | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: A pointer to a new array object. | 
|  | *  Failure: NULL, if any parameter is invalid or memory allocation fails. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayDestroyDescriptor (OLEAUT32.38) | 
|  | * | 
|  | * Destroy a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa [I] SafeArray to destroy. | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. The resources used by the array are freed. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | 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 */ | 
|  |  | 
|  | if (!SAFEARRAY_Free(lpv)) | 
|  | return E_UNEXPECTED; | 
|  | } | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayLock (OLEAUT32.21) | 
|  | * | 
|  | * Increment the lock counter of a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa [O] SafeArray to lock | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. The array lock is incremented. | 
|  | *  Failure: E_INVALIDARG if psa is NULL, or E_UNEXPECTED if too many locks | 
|  | *           are held on the array at once. | 
|  | * | 
|  | * NOTES | 
|  | *  In Win32 these locks are not thread safe. | 
|  | *  See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayLock(SAFEARRAY *psa) | 
|  | { | 
|  | ULONG ulLocks; | 
|  |  | 
|  | TRACE("(%p)\n", psa); | 
|  |  | 
|  | if (!psa) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | ulLocks = InterlockedIncrement( (LONG*) &psa->cLocks); | 
|  |  | 
|  | if (ulLocks > 0xffff) /* Maximum of 16384 locks at a time */ | 
|  | { | 
|  | WARN("Out of locks!\n"); | 
|  | InterlockedDecrement( (LONG*) &psa->cLocks); | 
|  | return E_UNEXPECTED; | 
|  | } | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayUnlock (OLEAUT32.22) | 
|  | * | 
|  | * Decrement the lock counter of a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa [O] SafeArray to unlock | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. The array lock is decremented. | 
|  | *  Failure: E_INVALIDARG if psa is NULL, or E_UNEXPECTED if no locks are | 
|  | *           held on the array. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayUnlock(SAFEARRAY *psa) | 
|  | { | 
|  | TRACE("(%p)\n", psa); | 
|  |  | 
|  | if (!psa) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | if (InterlockedDecrement( (LONG*) &psa->cLocks) < 0) | 
|  | { | 
|  | WARN("Unlocked but no lock held!\n"); | 
|  | InterlockedIncrement( (LONG*) &psa->cLocks); | 
|  | return E_UNEXPECTED; | 
|  | } | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayPutElement (OLEAUT32.26) | 
|  | * | 
|  | * Put an item into a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa       [I] SafeArray to insert into | 
|  | *  rgIndices [I] Indices to insert at | 
|  | *  pvData    [I] Data to insert | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. The item is inserted | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayPutElement(SAFEARRAY *psa, LONG *rgIndices, void *pvData) | 
|  | { | 
|  | HRESULT hRet; | 
|  |  | 
|  | TRACE("(%p,%p,%p)\n", psa, rgIndices, pvData); | 
|  |  | 
|  | if (!psa || !rgIndices) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | hRet = SafeArrayLock(psa); | 
|  |  | 
|  | if (SUCCEEDED(hRet)) | 
|  | { | 
|  | PVOID lpvDest; | 
|  |  | 
|  | hRet = SafeArrayPtrOfIndex(psa, rgIndices, &lpvDest); | 
|  |  | 
|  | if (SUCCEEDED(hRet)) | 
|  | { | 
|  | if (psa->fFeatures & FADF_VARIANT) | 
|  | { | 
|  | VARIANT* lpVariant = pvData; | 
|  | VARIANT* lpDest = lpvDest; | 
|  |  | 
|  | hRet = VariantClear(lpDest); | 
|  | if (FAILED(hRet)) FIXME("VariantClear failed with 0x%x\n", hRet); | 
|  | hRet = VariantCopy(lpDest, lpVariant); | 
|  | if (FAILED(hRet)) FIXME("VariantCopy failed with 0x%x\n", hRet); | 
|  | } | 
|  | else if (psa->fFeatures & FADF_BSTR) | 
|  | { | 
|  | BSTR  lpBstr = (BSTR)pvData; | 
|  | BSTR* lpDest = lpvDest; | 
|  |  | 
|  | SysFreeString(*lpDest); | 
|  |  | 
|  | *lpDest = SysAllocStringByteLen((char*)lpBstr, SysStringByteLen(lpBstr)); | 
|  | if (!*lpDest) | 
|  | hRet = E_OUTOFMEMORY; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (psa->fFeatures & (FADF_UNKNOWN|FADF_DISPATCH)) | 
|  | { | 
|  | LPUNKNOWN  lpUnknown = pvData; | 
|  | LPUNKNOWN *lpDest = lpvDest; | 
|  |  | 
|  | if (lpUnknown) | 
|  | IUnknown_AddRef(lpUnknown); | 
|  | if (*lpDest) | 
|  | IUnknown_Release(*lpDest); | 
|  | *lpDest = lpUnknown; | 
|  | } else { | 
|  | /* Copy the data over */ | 
|  | memcpy(lpvDest, pvData, psa->cbElements); | 
|  | } | 
|  | } | 
|  | } | 
|  | SafeArrayUnlock(psa); | 
|  | } | 
|  | return hRet; | 
|  | } | 
|  |  | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayGetElement (OLEAUT32.25) | 
|  | * | 
|  | * Get an item from a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa       [I] SafeArray to get from | 
|  | *  rgIndices [I] Indices to get from | 
|  | *  pvData    [O] Destination for data | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. The item data is returned in pvData. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayGetElement(SAFEARRAY *psa, LONG *rgIndices, void *pvData) | 
|  | { | 
|  | HRESULT hRet; | 
|  |  | 
|  | TRACE("(%p,%p,%p)\n", psa, rgIndices, pvData); | 
|  |  | 
|  | if (!psa || !rgIndices || !pvData) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | hRet = SafeArrayLock(psa); | 
|  |  | 
|  | if (SUCCEEDED(hRet)) | 
|  | { | 
|  | PVOID lpvSrc; | 
|  |  | 
|  | hRet = SafeArrayPtrOfIndex(psa, rgIndices, &lpvSrc); | 
|  |  | 
|  | if (SUCCEEDED(hRet)) | 
|  | { | 
|  | if (psa->fFeatures & FADF_VARIANT) | 
|  | { | 
|  | VARIANT* lpVariant = lpvSrc; | 
|  | VARIANT* lpDest = pvData; | 
|  |  | 
|  | /* The original content of pvData is ignored. */ | 
|  | V_VT(lpDest) = VT_EMPTY; | 
|  | hRet = VariantCopy(lpDest, lpVariant); | 
|  | if (FAILED(hRet)) FIXME("VariantCopy failed with 0x%x\n", hRet); | 
|  | } | 
|  | else if (psa->fFeatures & FADF_BSTR) | 
|  | { | 
|  | BSTR* lpBstr = lpvSrc; | 
|  | BSTR* lpDest = pvData; | 
|  |  | 
|  | if (*lpBstr) | 
|  | { | 
|  | *lpDest = SysAllocStringByteLen((char*)*lpBstr, SysStringByteLen(*lpBstr)); | 
|  | if (!*lpBstr) | 
|  | hRet = E_OUTOFMEMORY; | 
|  | } | 
|  | else | 
|  | *lpDest = NULL; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (psa->fFeatures & (FADF_UNKNOWN|FADF_DISPATCH)) | 
|  | { | 
|  | LPUNKNOWN *lpUnknown = lpvSrc; | 
|  |  | 
|  | if (*lpUnknown) | 
|  | IUnknown_AddRef(*lpUnknown); | 
|  | } | 
|  | /* Copy the data over */ | 
|  | memcpy(pvData, lpvSrc, psa->cbElements); | 
|  | } | 
|  | } | 
|  | SafeArrayUnlock(psa); | 
|  | } | 
|  | return hRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayGetUBound (OLEAUT32.19) | 
|  | * | 
|  | * Get the upper bound for a given SafeArray dimension | 
|  | * | 
|  | * PARAMS | 
|  | *  psa      [I] Array to get dimension upper bound from | 
|  | *  nDim     [I] The dimension number to get the upper bound of | 
|  | *  plUbound [O] Destination for the upper bound | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. plUbound contains the dimensions upper bound. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayGetLBound (OLEAUT32.20) | 
|  | * | 
|  | * Get the lower bound for a given SafeArray dimension | 
|  | * | 
|  | * PARAMS | 
|  | *  psa      [I] Array to get dimension lower bound from | 
|  | *  nDim     [I] The dimension number to get the lowe bound of | 
|  | *  plLbound [O] Destination for the lower bound | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. plUbound contains the dimensions lower bound. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayGetDim (OLEAUT32.17) | 
|  | * | 
|  | * Get the number of dimensions in a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa [I] Array to get the dimensions of | 
|  | * | 
|  | * RETURNS | 
|  | *  The number of array dimensions in psa, or 0 if psa is NULL. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | UINT WINAPI SafeArrayGetDim(SAFEARRAY *psa) | 
|  | { | 
|  | TRACE("(%p) returning %d\n", psa, psa ? psa->cDims : 0u); | 
|  | return psa ? psa->cDims : 0; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayGetElemsize (OLEAUT32.18) | 
|  | * | 
|  | * Get the size of an element in a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa [I] Array to get the element size from | 
|  | * | 
|  | * RETURNS | 
|  | *  The size of a single element in psa, or 0 if psa is NULL. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | UINT WINAPI SafeArrayGetElemsize(SAFEARRAY *psa) | 
|  | { | 
|  | TRACE("(%p) returning %d\n", psa, psa ? psa->cbElements : 0u); | 
|  | return psa ? psa->cbElements : 0; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayAccessData (OLEAUT32.23) | 
|  | * | 
|  | * Lock a SafeArray and return a pointer to its data. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa     [I] Array to get the data pointer from | 
|  | *  ppvData [O] Destination for the arrays data pointer | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. ppvData contains the arrays data pointer, and the array | 
|  | *           is locked. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayAccessData(SAFEARRAY *psa, void **ppvData) | 
|  | { | 
|  | TRACE("(%p,%p)\n", psa, ppvData); | 
|  |  | 
|  | if(!psa || !ppvData) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | if (SUCCEEDED(SafeArrayLock(psa))) | 
|  | { | 
|  | *ppvData = psa->pvData; | 
|  | return S_OK; | 
|  | } | 
|  | *ppvData = NULL; | 
|  | return E_UNEXPECTED; | 
|  | } | 
|  |  | 
|  |  | 
|  | /************************************************************************* | 
|  | *		SafeArrayUnaccessData (OLEAUT32.24) | 
|  | * | 
|  | * Unlock a SafeArray after accessing its data. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa     [I] Array to unlock | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. The array is unlocked. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayUnaccessData(SAFEARRAY *psa) | 
|  | { | 
|  | TRACE("(%p)\n", psa); | 
|  | return SafeArrayUnlock(psa); | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		SafeArrayPtrOfIndex (OLEAUT32.148) | 
|  | * | 
|  | * Get the address of an item in a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa       [I] Array to get the items address from | 
|  | *  rgIndices [I] Index of the item in the array | 
|  | *  ppvData   [O] Destination for item address | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. ppvData contains a pointer to the item. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | *  This function does not lock the array. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayPtrOfIndex(SAFEARRAY *psa, LONG *rgIndices, void **ppvData) | 
|  | { | 
|  | USHORT dim; | 
|  | ULONG cell = 0, dimensionSize = 1; | 
|  | SAFEARRAYBOUND* psab; | 
|  | LONG c1; | 
|  |  | 
|  | TRACE("(%p,%p,%p)\n", psa, rgIndices, ppvData); | 
|  |  | 
|  | /* The general formula for locating the cell number of an entry in an n | 
|  | * dimensional array (where cn = coordinate in dimension dn) is: | 
|  | * | 
|  | * c1 + c2 * sizeof(d1) + c3 * sizeof(d2) ... + cn * sizeof(c(n-1)) | 
|  | * | 
|  | * We calculate the size of the last dimension at each step through the | 
|  | * dimensions to avoid recursing to calculate the last dimensions size. | 
|  | */ | 
|  | if (!psa || !rgIndices || !ppvData) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | psab = psa->rgsabound + psa->cDims - 1; | 
|  | c1 = *rgIndices++; | 
|  |  | 
|  | if (c1 < psab->lLbound || c1 >= psab->lLbound + (LONG)psab->cElements) | 
|  | return DISP_E_BADINDEX; /* Initial index out of bounds */ | 
|  |  | 
|  | for (dim = 1; dim < psa->cDims; dim++) | 
|  | { | 
|  | dimensionSize *= psab->cElements; | 
|  |  | 
|  | psab--; | 
|  |  | 
|  | if (!psab->cElements || | 
|  | *rgIndices < psab->lLbound || | 
|  | *rgIndices >= psab->lLbound + (LONG)psab->cElements) | 
|  | return DISP_E_BADINDEX; /* Index out of bounds */ | 
|  |  | 
|  | cell += (*rgIndices - psab->lLbound) * dimensionSize; | 
|  | rgIndices++; | 
|  | } | 
|  |  | 
|  | cell += (c1 - psa->rgsabound[psa->cDims - 1].lLbound); | 
|  |  | 
|  | *ppvData = (char*)psa->pvData + cell * psa->cbElements; | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		SafeArrayDestroyData (OLEAUT32.39) | 
|  | * | 
|  | * Destroy the data associated with a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa [I] Array to delete the data from | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. All items and the item data are freed. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayDestroyData(SAFEARRAY *psa) | 
|  | { | 
|  | 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 */ | 
|  | if (FAILED(SAFEARRAY_DestroyData(psa, 0))) | 
|  | return E_UNEXPECTED; | 
|  |  | 
|  | 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)) | 
|  | { | 
|  | if (!SAFEARRAY_Free(psa->pvData)) | 
|  | return E_UNEXPECTED; | 
|  | psa->pvData = NULL; | 
|  | } | 
|  | else | 
|  | psa->fFeatures |= FADF_DATADELETED; /* Mark the data deleted */ | 
|  |  | 
|  | } | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		SafeArrayCopyData (OLEAUT32.412) | 
|  | * | 
|  | * Copy all data from one SafeArray to another. | 
|  | * | 
|  | * PARAMS | 
|  | *  psaSource [I] Source for copy | 
|  | *  psaTarget [O] Destination for copy | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. psaTarget contains a copy of psaSource. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | *  The two arrays must have the same number of dimensions and elements. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayCopyData(SAFEARRAY *psaSource, SAFEARRAY *psaTarget) | 
|  | { | 
|  | int dim; | 
|  |  | 
|  | TRACE("(%p,%p)\n", psaSource, psaTarget); | 
|  |  | 
|  | if (!psaSource || !psaTarget || | 
|  | psaSource->cDims != psaTarget->cDims || | 
|  | psaSource->cbElements != psaTarget->cbElements) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | /* Each dimension must be the same size */ | 
|  | for (dim = psaSource->cDims - 1; dim >= 0 ; dim--) | 
|  | if (psaSource->rgsabound[dim].cElements != | 
|  | psaTarget->rgsabound[dim].cElements) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | if (SUCCEEDED(SAFEARRAY_DestroyData(psaTarget, 0)) && | 
|  | SUCCEEDED(SAFEARRAY_CopyData(psaSource, psaTarget))) | 
|  | return S_OK; | 
|  | return E_UNEXPECTED; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		SafeArrayDestroy (OLEAUT32.16) | 
|  | * | 
|  | * Destroy a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa [I] Array to destroy | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. All resources used by the array are freed. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayDestroy(SAFEARRAY *psa) | 
|  | { | 
|  | TRACE("(%p)\n", psa); | 
|  |  | 
|  | if(!psa) | 
|  | return S_OK; | 
|  |  | 
|  | if(psa->cLocks > 0) | 
|  | return DISP_E_ARRAYISLOCKED; | 
|  |  | 
|  | /* Native doesn't check to see if the free succeeds */ | 
|  | SafeArrayDestroyData(psa); | 
|  | SafeArrayDestroyDescriptor(psa); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		SafeArrayCopy (OLEAUT32.27) | 
|  | * | 
|  | * Make a duplicate of a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa     [I] Source for copy | 
|  | *  ppsaOut [O] Destination for new copy | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. ppsaOut contains a copy of the array. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayCopy(SAFEARRAY *psa, SAFEARRAY **ppsaOut) | 
|  | { | 
|  | HRESULT hRet; | 
|  |  | 
|  | TRACE("(%p,%p)\n", psa, ppsaOut); | 
|  |  | 
|  | if (!ppsaOut) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | *ppsaOut = NULL; | 
|  |  | 
|  | if (!psa) | 
|  | return S_OK; /* Handles copying of NULL arrays */ | 
|  |  | 
|  | if (!psa->cbElements) | 
|  | { | 
|  | ERR("not copying an array of 0 elements\n"); | 
|  | return E_INVALIDARG; | 
|  | } | 
|  |  | 
|  | if (psa->fFeatures & (FADF_RECORD|FADF_HAVEIID|FADF_HAVEVARTYPE)) | 
|  | { | 
|  | VARTYPE vt; | 
|  | if (FAILED(SafeArrayGetVartype(psa, &vt))) | 
|  | hRet = E_UNEXPECTED; | 
|  | else | 
|  | hRet = SafeArrayAllocDescriptorEx(vt, psa->cDims, ppsaOut); | 
|  | } | 
|  | else | 
|  | { | 
|  | hRet = SafeArrayAllocDescriptor(psa->cDims, ppsaOut); | 
|  | if (SUCCEEDED(hRet)) | 
|  | { | 
|  | (*ppsaOut)->fFeatures = psa->fFeatures & ~FADF_CREATEVECTOR; | 
|  | (*ppsaOut)->cbElements = psa->cbElements; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (SUCCEEDED(hRet)) | 
|  | { | 
|  | /* Copy dimension bounds */ | 
|  | memcpy((*ppsaOut)->rgsabound, psa->rgsabound, psa->cDims * sizeof(SAFEARRAYBOUND)); | 
|  |  | 
|  | (*ppsaOut)->pvData = SAFEARRAY_Malloc(SAFEARRAY_GetCellCount(psa) * psa->cbElements); | 
|  |  | 
|  | if ((*ppsaOut)->pvData) | 
|  | { | 
|  | hRet = SAFEARRAY_CopyData(psa, *ppsaOut); | 
|  |  | 
|  | if (SUCCEEDED(hRet)) | 
|  | return hRet; | 
|  |  | 
|  | SAFEARRAY_Free((*ppsaOut)->pvData); | 
|  | } | 
|  | SafeArrayDestroyDescriptor(*ppsaOut); | 
|  | } | 
|  | *ppsaOut = NULL; | 
|  | return hRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		SafeArrayRedim (OLEAUT32.40) | 
|  | * | 
|  | * Changes the characteristics of the last dimension of a SafeArray | 
|  | * | 
|  | * PARAMS | 
|  | *  psa      [I] Array to change | 
|  | *  psabound [I] New bound details for the last dimension | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. psa is updated to reflect the new bounds. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | HRESULT WINAPI SafeArrayRedim(SAFEARRAY *psa, SAFEARRAYBOUND *psabound) | 
|  | { | 
|  | SAFEARRAYBOUND *oldBounds; | 
|  |  | 
|  | TRACE("(%p,%p)\n", psa, psabound); | 
|  |  | 
|  | if (!psa || psa->fFeatures & FADF_FIXEDSIZE || !psabound) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | if (psa->cLocks > 0) | 
|  | return DISP_E_ARRAYISLOCKED; | 
|  |  | 
|  | if (FAILED(SafeArrayLock(psa))) | 
|  | return E_UNEXPECTED; | 
|  |  | 
|  | oldBounds = psa->rgsabound; | 
|  | oldBounds->lLbound = psabound->lLbound; | 
|  |  | 
|  | if (psabound->cElements != oldBounds->cElements) | 
|  | { | 
|  | if (psabound->cElements < oldBounds->cElements) | 
|  | { | 
|  | /* Shorten the final dimension. */ | 
|  | ULONG ulStartCell = psabound->cElements * | 
|  | (SAFEARRAY_GetCellCount(psa) / oldBounds->cElements); | 
|  | SAFEARRAY_DestroyData(psa, ulStartCell); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Lengthen the final dimension */ | 
|  | ULONG ulOldSize, ulNewSize; | 
|  | PVOID pvNewData; | 
|  |  | 
|  | ulOldSize = SAFEARRAY_GetCellCount(psa) * psa->cbElements; | 
|  | if (ulOldSize) | 
|  | ulNewSize = (ulOldSize / oldBounds->cElements) * psabound->cElements; | 
|  | else { | 
|  | int oldelems = oldBounds->cElements; | 
|  | oldBounds->cElements = psabound->cElements; | 
|  | ulNewSize = SAFEARRAY_GetCellCount(psa) * psa->cbElements; | 
|  | oldBounds->cElements = oldelems; | 
|  | } | 
|  |  | 
|  | if (!(pvNewData = SAFEARRAY_Malloc(ulNewSize))) | 
|  | { | 
|  | SafeArrayUnlock(psa); | 
|  | return E_UNEXPECTED; | 
|  | } | 
|  |  | 
|  | memcpy(pvNewData, psa->pvData, ulOldSize); | 
|  | SAFEARRAY_Free(psa->pvData); | 
|  | psa->pvData = pvNewData; | 
|  | } | 
|  | oldBounds->cElements = psabound->cElements; | 
|  | } | 
|  |  | 
|  | SafeArrayUnlock(psa); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		SafeArrayGetVartype (OLEAUT32.77) | 
|  | * | 
|  | * Get the type of the items in a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa [I] Array to get the type from | 
|  | *  pvt [O] Destination for the type | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. pvt contains the type of the items. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		SafeArraySetRecordInfo (OLEAUT32.@) | 
|  | * | 
|  | * Set the record info for a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa    [I] Array to set the record info for | 
|  | *  pRinfo [I] Record info | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. The record info is stored with the array. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		SafeArrayGetRecordInfo (OLEAUT32.@) | 
|  | * | 
|  | * Get the record info from a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa    [I] Array to get the record info from | 
|  | *  pRinfo [O] Destination for the record info | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. pRinfo contains the record info, or NULL if there was none. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		SafeArraySetIID (OLEAUT32.@) | 
|  | * | 
|  | * Set the IID for a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa  [I] Array to set the IID from | 
|  | *  guid [I] IID | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. The IID is stored with the array | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		SafeArrayGetIID (OLEAUT32.@) | 
|  | * | 
|  | * Get the IID from a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa   [I] Array to get the ID from | 
|  | *  pGuid [O] Destination for the IID | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. pRinfo contains the IID, or NULL if there was none. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		VectorFromBstr (OLEAUT32.@) | 
|  | * | 
|  | * Create a SafeArray Vector from the bytes of a BSTR. | 
|  | * | 
|  | * PARAMS | 
|  | *  bstr [I] String to get bytes from | 
|  | *  ppsa [O] Destination for the array | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. ppsa contains the strings bytes as a VT_UI1 array. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /************************************************************************ | 
|  | *		BstrFromVector (OLEAUT32.@) | 
|  | * | 
|  | * Create a BSTR from a SafeArray. | 
|  | * | 
|  | * PARAMS | 
|  | *  psa   [I] Source array | 
|  | *  pbstr [O] Destination for output BSTR | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. pbstr contains the arrays data. | 
|  | *  Failure: An HRESULT error code indicating the error. | 
|  | * | 
|  | * NOTES | 
|  | *  psa must be a 1 dimensional array of a 1 byte type. | 
|  | * | 
|  | * NOTES | 
|  | * See SafeArray. | 
|  | */ | 
|  | 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; | 
|  | } |