safearray

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

This is a rewrite of the SafeArray code that implements the remaining
stubs and updates variant coercion to use these calls when asked to
convert an array.

I'm interested if this causes any regressions for anyone. Its rather
difficult to test all the possible permutations of arrays.

Note there is some overlap between the new and existing tests which I
will attempt to remove later. This patch is already quite big, and as
it is, shows that all the existing tests pass with the new code

License: X11

ChangeLog:

  Jon Griffiths <jon_p_griffiths@xxxxxxxxx>

  +dlls/oleaut32/oleaut32.spec dlls/oleaut32/safearray.c 
    Rewrite: Support I8/UI8,RECORD
    Store array bounds in the same order as native
    Add CreateEx, CreateVectorEx, VectorFromBstr, BstrFromVector

  +dlls/oleaut32/variant.c
    Use the SafeArray conversion functions when coercing arrays
    Simplify VariantChangeTypeEx.
    Replace calls to dump_Variant() with TRACE's

  +dlls/oleaut32/tests/safearray.c
    I8/UI8 are accepted in arrays in recent native dlls.


    Test UDTs, array coercion and new functions

=====
"Don't wait for the seas to part, or messiahs to come;
 Don't you sit around and waste this chance..." - Live

jon_p_griffiths@xxxxxxxxx

__________________________________
Do you Yahoo!?
New Yahoo! Photos - easier uploading and sharing.
http://photos.yahoo.com/
diff -ur --minimal wine/dlls/oleaut32/oleaut32.spec wine-develop/dlls/oleaut32/oleaut32.spec
--- wine/dlls/oleaut32/oleaut32.spec	2003-12-11 16:24:02.000000000 +0000
+++ wine-develop/dlls/oleaut32/oleaut32.spec	2003-12-11 19:42:50.000000000 +0000
@@ -39,8 +39,8 @@
 39 stdcall SafeArrayDestroyData(ptr)
 40 stdcall SafeArrayRedim(ptr ptr)
 41 stdcall SafeArrayAllocDescriptorEx(long long ptr)
-42 stub SafeArrayCreateEx
-43 stub SafeArrayCreateVectorEx
+42 stdcall SafeArrayCreateEx(long long ptr ptr)
+43 stdcall SafeArrayCreateVectorEx(long long long ptr)
 44 stdcall SafeArraySetRecordInfo(ptr ptr)
 45 stdcall SafeArrayGetRecordInfo(ptr ptr)
 46 stdcall VarParseNumFromStr(wstr long long ptr ptr)
@@ -385,8 +385,8 @@
 410 stdcall -private DllCanUnloadNow() OLEAUT32_DllCanUnloadNow
 411 stdcall SafeArrayCreateVector(long long long)
 412 stdcall SafeArrayCopyData(ptr ptr)
-413 stub VectorFromBstr
-414 stub BstrFromVector
+413 stdcall VectorFromBstr(ptr ptr)
+414 stdcall BstrFromVector(ptr ptr)
 415 stdcall OleIconToCursor(long long)
 416 stdcall OleCreatePropertyFrameIndirect(ptr)
 417 stdcall OleCreatePropertyFrame(ptr long long ptr long ptr long ptr ptr long ptr)
diff -ur --minimal wine/dlls/oleaut32/safearray.c wine-develop/dlls/oleaut32/safearray.c
--- wine/dlls/oleaut32/safearray.c	2003-09-11 16:11:10.000000000 +0000
+++ wine-develop/dlls/oleaut32/safearray.c	2003-12-12 16:52:35.000000000 +0000
@@ -1,10 +1,10 @@
 /*************************************************************************
- * OLE Automation
- * SafeArray Implementation
+ * OLE Automation - SafeArray
  *
- * This file contains the implementation of the SafeArray interface.
+ * This file contains the implementation of the SafeArray functions.
  *
  * Copyright 1999 Sylvain St-Germain
+ * 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
@@ -23,1307 +23,1702 @@
 /* Memory Layout of a SafeArray:
  *
  * -0x10: start of memory.
- * -0x10: GUID	for VT_DISPATCH and VT_UNKNOWN safearrays (if FADF_HAVEIID)
+ * -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))
+ *  -0x4: IRecordInfo* iface;  (if FADF_RECORD, for VT_RECORD (can be NULL))
  *  0x00: SAFEARRAY,
  *  0x10: SAFEARRAYBOUNDS[0...]
  */
 
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
 #include <stdarg.h>
 #include <stdio.h>
-#include <string.h>
 #include "windef.h"
 #include "winerror.h"
 #include "winbase.h"
 #include "oleauto.h"
 #include "wine/debug.h"
+#include "variant.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(ole);
 
-#define SYSDUPSTRING(str) SysAllocStringLen((str), SysStringLen(str))
+/************************************************************************
+ * 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 seperately from
+ * the array header. This is the most flexable 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()
+ */
 
-/* Locally used methods */
-static ULONG
-calcDisplacement(LONG *coor, SAFEARRAYBOUND *mat, LONG dim);
+/* Undocumented hidden space before the start of a SafeArray descriptor */
+#define SAFEARRAY_HIDDEN_SIZE sizeof(GUID)
 
-static BOOL
-isPointer(USHORT feature);
+/* Value returned by SAFEARRAY_GetCellCount if a dimension is invalid */
+#define SAFEARRAY_INVALID_CELLS ~0UL
 
-static INT
-getFeatures(VARTYPE vt);
+/* 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);
+}
 
-static BOOL
-validCoordinate(LONG *coor, SAFEARRAY *psa);
+/* Free memory */
+static inline BOOL SAFEARRAY_Free(LPVOID lpData)
+{
+  return HeapFree(GetProcessHeap(), 0, lpData);
+}
 
-static BOOL
-resizeSafeArray(SAFEARRAY *psa, LONG lDelta);
+/* 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;
+}
 
-static BOOL
-validArg(SAFEARRAY *psa);
+/* 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;
+}
 
-static ULONG
-getArraySize(SAFEARRAY *psa);
+/* Get the hidden data from an array */
+static inline DWORD SAFEARRAY_GetHiddenDWORD(SAFEARRAY* psa)
+{
+  LPDWORD lpDw = (LPDWORD)psa;
+  return lpDw[-1];
+}
 
-static HRESULT
-duplicateData(SAFEARRAY *psa, SAFEARRAY *ppsaOut);
+/* Get the number of cells in a SafeArray */
+static ULONG SAFEARRAY_GetCellCount(const SAFEARRAY *psa)
+{
+  SAFEARRAYBOUND* psab = psa->rgsabound;
+  USHORT cCount = psa->cDims;
+  ULONG ulNumCells = 1;
 
-/* Association between VARTYPE and their size.
-   A size of zero is defined for the unsupported types.  */
+  while (cCount--)
+  {
+    if (!psab->cElements)
+    {
+      ERR("Dimension has size=0! Please report.\n");
+      return SAFEARRAY_INVALID_CELLS;
+    }
+    ulNumCells *= psab->cElements;
+    psab++;
+  }
+  return ulNumCells;
+}
 
-#define VARTYPE_NOT_SUPPORTED 0
-static const ULONG VARTYPE_SIZE[] =
+/* Get the 0 based index of an index into a dimension */
+static inline ULONG SAFEARRAY_GetDimensionIndex(SAFEARRAYBOUND *psab, ULONG ulIndex)
 {
-  /* 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 */
-sizeof(BSTR),           /* VT_BSTR     [V][T][P][S] OLE Automation string*/
-sizeof(LPDISPATCH),     /* VT_DISPATCH [V][T][P][S] IDispatch *	*/
-4,                      /* VT_ERROR    [V][T]   [S] SCODE	*/
-2,                      /* VT_BOOL     [V][T][P][S] True=-1, False=0*/
-sizeof(VARIANT),        /* VT_VARIANT  [V][T][P][S] VARIANT *	*/
-sizeof(LPUNKNOWN),      /* VT_UNKNOWN  [V][T]   [S] IUnknown * */
-sizeof(DECIMAL),        /* VT_DECIMAL  [V][T]   [S] 16 byte fixed point	*/
-VARTYPE_NOT_SUPPORTED,                         /* no VARTYPE here.....	*/
-1,			/* VT_I1          [T]   [S] signed char		*/
-1,                      /* VT_UI1      [V][T][P][S] unsigned char	*/
-2,			/* VT_UI2         [T][P][S] unsigned short	*/
-4,			/* VT_UI4         [T][P][S] unsigned int	*/
-VARTYPE_NOT_SUPPORTED,	/* VT_I8          [T][P]    signed 64-bit int			*/
-VARTYPE_NOT_SUPPORTED,	/* VT_UI8         [T][P]    unsigned 64-bit int		*/
-sizeof(INT),		/* VT_INT         [T]       signed machine int		*/
-sizeof(UINT),		/* 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,	/* 32 */
-VARTYPE_NOT_SUPPORTED,  /* 33 */
-VARTYPE_NOT_SUPPORTED,  /* 34 */
-VARTYPE_NOT_SUPPORTED,  /* 35 */
-VARTYPE_NOT_SUPPORTED,  /* VT_RECORD                record */
-VARTYPE_NOT_SUPPORTED,  /* 37 */
-VARTYPE_NOT_SUPPORTED,  /* 38 */
-VARTYPE_NOT_SUPPORTED,  /* 39 */
-VARTYPE_NOT_SUPPORTED,  /* 40 */
-VARTYPE_NOT_SUPPORTED,  /* 41 */
-VARTYPE_NOT_SUPPORTED,  /* 42 */
-VARTYPE_NOT_SUPPORTED,  /* 43 */
-VARTYPE_NOT_SUPPORTED,  /* 44 */
-VARTYPE_NOT_SUPPORTED,  /* 45 */
-VARTYPE_NOT_SUPPORTED,  /* 46 */
-VARTYPE_NOT_SUPPORTED,  /* 47 */
-VARTYPE_NOT_SUPPORTED,  /* 48 */
-VARTYPE_NOT_SUPPORTED,  /* 49 */
-VARTYPE_NOT_SUPPORTED,  /* 50 */
-VARTYPE_NOT_SUPPORTED,  /* 51 */
-VARTYPE_NOT_SUPPORTED,  /* 52 */
-VARTYPE_NOT_SUPPORTED,  /* 53 */
-VARTYPE_NOT_SUPPORTED,  /* 54 */
-VARTYPE_NOT_SUPPORTED,  /* 55 */
-VARTYPE_NOT_SUPPORTED,  /* 56 */
-VARTYPE_NOT_SUPPORTED,  /* 57 */
-VARTYPE_NOT_SUPPORTED,  /* 58 */
-VARTYPE_NOT_SUPPORTED,  /* 59 */
-VARTYPE_NOT_SUPPORTED,  /* 60 */
-VARTYPE_NOT_SUPPORTED,  /* 61 */
-VARTYPE_NOT_SUPPORTED,  /* 62 */
-VARTYPE_NOT_SUPPORTED,  /* 63 */
-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			*/
-};
+  return ulIndex - psab->lLbound;
+}
 
-static const int LAST_VARTYPE = sizeof(VARTYPE_SIZE)/sizeof(VARTYPE_SIZE[0]);
+/* Get the size of a dimension in cells */
+static inline ULONG SAFEARRAY_GetDimensionCells(SAFEARRAY *psa, ULONG ulDim)
+{
+  ULONG size = psa->rgsabound[0].cElements;
+
+  while (ulDim)
+  {
+    size *= psa->rgsabound[ulDim].cElements;
+    ulDim--;
+  }
+  return size;
+}
+
+/* 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, SAFEARRAYBOUND *rgsabound, ULONG ulSize)
+{
+  SAFEARRAY *psa = NULL;
+
+  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;
+    }
+
+    memcpy(psa->rgsabound, rgsabound, cDims * sizeof(SAFEARRAYBOUND));
+
+    if (ulSize)
+      psa->cbElements = ulSize;
+
+    if (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 (cElements && (vt == VT_RECORD || ulSize))
+  {
+    /* 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;
+      }
+    }
+  }
+  else if (!cElements)
+  {
+    ERR("Creating vector of size 0! Please report.\n");
+  }
+  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 (ulCellCount == SAFEARRAY_INVALID_CELLS || ulStartCell > ulCellCount)
+      return E_UNEXPECTED;
+
+    ulCellCount -= ulStartCell;
+
+    if (psa->fFeatures & (FADF_UNKNOWN|FADF_DISPATCH))
+    {
+      LPUNKNOWN *lpUnknown = (LPUNKNOWN *)psa->pvData + ulStartCell * psa->cbElements;
+
+      while(ulCellCount--)
+      {
+        if (*lpUnknown)
+          IUnknown_Release(*lpUnknown);
+        lpUnknown++;
+      }
+    }
+    else if (psa->fFeatures & (FADF_RECORD))
+    {
+      IRecordInfo *lpRecInfo;
+
+      if (SUCCEEDED(SafeArrayGetRecordInfo(psa, &lpRecInfo)))
+      {
+        PBYTE pRecordData = (PBYTE)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 * psa->cbElements;
+
+      while(ulCellCount--)
+      {
+        if (*lpBstr)
+          SysFreeString(*lpBstr);
+        lpBstr++;
+      }
+    }
+    else if (psa->fFeatures & FADF_VARIANT)
+    {
+      VARIANT* lpVariant = (VARIANT*)psa->pvData + ulStartCell * psa->cbElements;
+
+      while(ulCellCount--)
+      {
+        VariantClear(lpVariant);
+        lpVariant++;
+      }
+    }
+  }
+  return S_OK;
+}
+
+/* Copy data items from one array to another */
+static HRESULT SAFEARRAY_CopyData(SAFEARRAY *psa, SAFEARRAY *dest)
+{
+  if (!psa->pvData || !dest->pvData || psa->fFeatures & FADF_DATADELETED)
+    return E_INVALIDARG;
+  else
+  {
+    ULONG ulCellCount = SAFEARRAY_GetCellCount(psa);
+
+    if (ulCellCount == SAFEARRAY_INVALID_CELLS)
+      return E_UNEXPECTED;
+
+    dest->fFeatures = (dest->fFeatures & FADF_CREATEVECTOR) |
+                      (psa->fFeatures & ~(FADF_CREATEVECTOR|FADF_DATADELETED));
+
+    if (psa->fFeatures & FADF_VARIANT)
+    {
+      VARIANT* lpVariant = (VARIANT*)psa->pvData;
+      VARIANT* lpDest = (VARIANT*)dest->pvData;
+
+      while(ulCellCount--)
+      {
+        VariantCopy(lpDest, lpVariant);
+        lpVariant++;
+        lpDest++;
+      }
+    }
+    else if (psa->fFeatures & FADF_BSTR)
+    {
+      BSTR* lpBstr = (BSTR*)psa->pvData;
+      BSTR* lpDest = (BSTR*)dest->pvData;
+
+      while(ulCellCount--)
+      {
+        if (*lpBstr)
+        {
+          *lpDest = SysAllocStringLen(*lpBstr, SysStringLen(*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 = (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 the appropriate amount of memory for the SafeArray descriptor
+ *
+ * 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)
+HRESULT WINAPI SafeArrayAllocDescriptor(UINT cDims, SAFEARRAY **ppsaOut)
 {
-  SAFEARRAYBOUND *sab;
-  LONG allocSize = 0;
-  char *ptr;
+  LONG allocSize;
 
-  if (!cDims || cDims >= 0x10000) /* 65536 appears to be the limit */
+  TRACE("(%d,%p)\n", cDims, ppsaOut);
+  
+  if (!cDims || cDims >= 0x10000) /* Maximum 65535 dimensions */
     return E_INVALIDARG;
+
   if (!ppsaOut)
     return E_POINTER;
 
-  /* GUID + SAFEARRAY + SAFEARRAYBOUND * (cDims -1)
-   * ( -1 because there is already one ( in SAFEARRAY struct
-   */
-  allocSize = sizeof(GUID) + sizeof(**ppsaOut) + (sizeof(*sab) * (cDims-1));
+  /* We need enough space for the header and its bounds */
+  allocSize = sizeof(SAFEARRAY) + sizeof(SAFEARRAYBOUND) * (cDims - 1);
 
-  /* Allocate memory for SAFEARRAY struc */
-  ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, allocSize);
-  if (!ptr)
-    return E_OUTOFMEMORY;
-  *ppsaOut = (SAFEARRAY *)(ptr + sizeof(GUID));
-  (*ppsaOut)->cDims	= cDims;
-  TRACE("(%d): %lu bytes allocated for descriptor.\n", cDims, allocSize);
+  if (FAILED(SAFEARRAY_AllocDescriptor(allocSize, ppsaOut)))
+    return E_UNEXPECTED;
 
-  return(S_OK);
+  (*ppsaOut)->cDims = cDims;
+
+  TRACE("(%d): %lu bytes allocated for descriptor.\n", cDims, allocSize);
+  return S_OK;
 }
 
 /*************************************************************************
  *		SafeArrayAllocDescriptorEx (OLEAUT32.41)
- * Allocate the appropriate amount of memory for the SafeArray descriptor
- * and also store information about the vartype before the returned pointer.
+ *
+ * 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 chack 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)
+HRESULT WINAPI SafeArrayAllocDescriptorEx(VARTYPE vt, UINT cDims, SAFEARRAY **ppsaOut)
 {
-  HRESULT hres;
+  ULONG cbElements;
+  HRESULT hRet = E_UNEXPECTED;
 
-  hres = SafeArrayAllocDescriptor (cDims, ppsaOut);
-  if (FAILED(hres))
-    return hres;
+  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");
 
-  switch (vt) {
-  case VT_DISPATCH:
-    (*ppsaOut)->fFeatures = FADF_HAVEIID;
-    SafeArraySetIID( *ppsaOut, &IID_IDispatch);
-    break;
-  case VT_UNKNOWN:
-    (*ppsaOut)->fFeatures = FADF_HAVEIID;
-    SafeArraySetIID( *ppsaOut, &IID_IUnknown);
-    break;
-  case VT_RECORD:
-    (*ppsaOut)->fFeatures = FADF_RECORD;
-    break;
-  default:
-    (*ppsaOut)->fFeatures = FADF_HAVEVARTYPE;
-    ((DWORD*)*ppsaOut)[-1] = vt;
-    break;
+  hRet = SafeArrayAllocDescriptor(cDims, ppsaOut);
+
+  if (SUCCEEDED(hRet))
+  {
+    SAFEARRAY_SetFeatures(vt, *ppsaOut);
+    (*ppsaOut)->cbElements = cbElements;
   }
-  return S_OK;
+  return hRet;
 }
 
 /*************************************************************************
  *		SafeArrayAllocData (OLEAUT32.37)
- * Allocate the appropriate amount of data for the SafeArray data
+ *
+ * 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 WINAPI SafeArrayAllocData(SAFEARRAY *psa)
 {
-  ULONG  ulWholeArraySize;   /* to store the size of the whole thing */
-
-  if(! validArg(psa))
-    return E_INVALIDARG;
-
-  ulWholeArraySize = getArraySize(psa);
+  HRESULT hRet = E_INVALIDARG;
+  
+  TRACE("(%p)\n", psa);
+  
+  if (psa)
+  {
+    ULONG ulSize = SAFEARRAY_GetCellCount(psa);
 
-  /* Allocate memory for the data itself */
-  if((psa->pvData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-        psa->cbElements*ulWholeArraySize)) == NULL)
-    return(E_UNEXPECTED);
+    hRet = E_OUTOFMEMORY;
 
-  TRACE("SafeArray: %lu bytes allocated for data at %p (%lu objects).\n",
-    psa->cbElements*ulWholeArraySize, psa->pvData, ulWholeArraySize);
+    if (ulSize != SAFEARRAY_INVALID_CELLS && psa->cbElements)
+    {
+      psa->pvData = SAFEARRAY_Malloc(ulSize * psa->cbElements);
 
-  return(S_OK);
+      if (psa->pvData)
+      {
+        hRet = S_OK;
+        TRACE("%lu bytes allocated for data at %p (%lu objects).\n",
+              ulSize * psa->cbElements, psa->pvData, ulSize);
+      }
+    }
+  }
+  return hRet;
 }
 
 /*************************************************************************
  *		SafeArrayCreate (OLEAUT32.15)
- * Create a SafeArray object by encapsulating AllocDescriptor and AllocData
+ *
+ * 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)
+SAFEARRAY* WINAPI SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND *rgsabound)
 {
-  SAFEARRAY *psa;
-  HRESULT   hRes;
-  USHORT    cDim;
-
-  TRACE("(%d, %d, %p)\n", vt, cDims, rgsabound);
+  TRACE("(%d->%s,%d,%p)\n", vt, debugstr_vt(vt), cDims, rgsabound);
 
-  /* Validate supported VARTYPE */
-  if ( (vt >= LAST_VARTYPE) ||
-       ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
+  if (vt == VT_RECORD)
     return NULL;
 
-  /* Allocate memory for the array descriptor */
-  if( FAILED( hRes = SafeArrayAllocDescriptorEx(vt, cDims, &psa)))
-    return NULL;
+  return SAFEARRAY_Create(vt, cDims, rgsabound, 0);
+}
 
-  /* setup data members... */
-  psa->cDims     = cDims;
-  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;
-  default: break;
+/*************************************************************************
+ *		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 = (IRecordInfo*)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->cLocks    = 0;
-  psa->pvData    = NULL;
-  psa->cbElements= VARTYPE_SIZE[vt];
+  psa = SAFEARRAY_Create(vt, cDims, rgsabound, ulSize);
 
-  /* 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;
+  if (pvExtra)
+  {
+    switch(vt)
+    {
+      case VT_RECORD:
+        SafeArraySetRecordInfo(psa, pvExtra);
+        break;
+      case VT_UNKNOWN:
+      case VT_DISPATCH:
+        SafeArraySetIID(psa, pvExtra);
+        break;
+    }
   }
+  return psa;
+}
 
-  /* allocate memory for the data... */
-  if( FAILED( hRes = SafeArrayAllocData(psa))) {
-    SafeArrayDestroyDescriptor(psa);
-    ERR("() : Failed to allocate the Safe Array data\n");
+/************************************************************************
+ *		SafeArrayCreateVector (OLEAUT32.411)
+ *
+ * Create a one dimensional, contigous 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,%ld,%ld\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, contigous 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 = (IRecordInfo*)pvExtra;
+  SAFEARRAY* psa;
+
+ TRACE("(%d->%s,%ld,%ld,%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);
 
-  return(psa);
+  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)
- * Frees the memory associated with the descriptor.
+ *
+ * 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)
+HRESULT WINAPI SafeArrayDestroyDescriptor(SAFEARRAY *psa)
 {
-  LPVOID ptr;
+  TRACE("(%p)\n", psa);
+    
+  if (psa)
+  {
+    LPVOID lpv = (char*)psa - SAFEARRAY_HIDDEN_SIZE;
 
-  /* Check for lockness before to free... */
-  if(psa->cLocks > 0)
-    return DISP_E_ARRAYISLOCKED;
+    if (psa->cLocks)
+      return DISP_E_ARRAYISLOCKED; /* Can't destroy a locked array */
 
-  /* The array is unlocked, then, deallocate memory */
-  ptr = ((IID*)psa)-1;
-  if(HeapFree( GetProcessHeap(), 0, ptr) == FALSE)
-    return E_UNEXPECTED;
-  return(S_OK);
-}
+    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
  *
- * 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
+ * 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)
+HRESULT WINAPI SafeArrayLock(SAFEARRAY *psa)
 {
-  if(! validArg(psa))
+  ULONG ulLocks;
+
+  TRACE("(%p)\n", psa);
+    
+  if (!psa)
     return E_INVALIDARG;
 
-  psa->cLocks++;
+  ulLocks = InterlockedIncrement(&psa->cLocks);
 
-  return(S_OK);
+  if (ulLocks > 0xffff) /* Maximum of 16384 locks at a time */
+  {
+    WARN("Out of locks!\n");
+    InterlockedDecrement(&psa->cLocks);
+    return E_UNEXPECTED;
+  }
+  return S_OK;
 }
 
 /*************************************************************************
  *		SafeArrayUnlock (OLEAUT32.22)
- * Decrement the lock counter
+ *
+ * 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)
+HRESULT WINAPI SafeArrayUnlock(SAFEARRAY *psa)
 {
-  if(! validArg(psa))
+  TRACE("(%p)\n", psa);
+  
+  if (!psa)
     return E_INVALIDARG;
 
-  if (psa->cLocks > 0)
-    psa->cLocks--;
-
-  return(S_OK);
+  if ((LONG)InterlockedDecrement(&psa->cLocks) < 0)
+  {
+    WARN("Unlocked but no lock held!\n");
+    InterlockedIncrement(&psa->cLocks);
+    return E_UNEXPECTED;
+  }
+  return S_OK;
 }
 
-
 /*************************************************************************
  *		SafeArrayPutElement (OLEAUT32.26)
- * Set the data at the given coordinate
+ *
+ * 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      *pv)
+HRESULT WINAPI SafeArrayPutElement(SAFEARRAY *psa, LONG *rgIndices, void *pvData)
 {
-  ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to
-                                         the desired one... */
-  PVOID elementStorageAddress = NULL; /* Address to store the data */
+  HRESULT hRet;
 
-  /* Validate the index given */
-  if(! validCoordinate(rgIndices, psa))
-    return DISP_E_BADINDEX;
-  if(! validArg(psa))
+  TRACE("(%p,%p,%p)\n", psa, rgIndices, pvData);
+
+  if (!psa || !rgIndices)
     return E_INVALIDARG;
 
-  if( SafeArrayLock(psa) == S_OK) {
+  if (!pvData)
+  {
+    ERR("Invalid pvData would crash under Win32!\n");
+    return E_INVALIDARG;
+  }
 
-    /* Figure out the number of items to skip */
-    stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
+  hRet = SafeArrayLock(psa);
 
-    /* Figure out the number of byte to skip ... */
-    elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
+  if (SUCCEEDED(hRet))
+  {
+    PVOID lpvDest;
 
-    if(isPointer(psa->fFeatures)) { /* increment ref count for this pointer */
+    hRet = SafeArrayPtrOfIndex(psa, rgIndices, &lpvDest);
 
-      *((PVOID*)elementStorageAddress) = *(PVOID*)pv;
-      IUnknown_AddRef( *(IUnknown**)pv);
+    if (SUCCEEDED(hRet))
+    {
+      if (psa->fFeatures & FADF_VARIANT)
+      {
+        VARIANT* lpVariant = (VARIANT*)pvData;
+        VARIANT* lpDest = (VARIANT*)lpvDest;
 
-    } else {
+        VariantClear(lpDest);
+        VariantCopy(lpDest, lpVariant);
+      }
+      else if (psa->fFeatures & FADF_BSTR)
+      {
+        BSTR* lpBstr = (BSTR*)pvData;
+        BSTR* lpDest = (BSTR*)lpvDest;
 
-      if(psa->fFeatures & FADF_BSTR) { /* Create a new object */
-        BSTR pbstrReAllocStr = NULL;
-        if(pv &&
-           ((pbstrReAllocStr = SYSDUPSTRING( (OLECHAR*)pv )) == NULL)) {
-          SafeArrayUnlock(psa);
-          return E_OUTOFMEMORY;
-        } else
-          *((BSTR*)elementStorageAddress) = pbstrReAllocStr;
+        if (*lpDest)
+         SysFreeString(*lpDest);
+
+        if (*lpBstr)
+        {
+          *lpDest = SysAllocStringLen(*lpBstr, SysStringLen(*lpBstr));
+          if (!*lpDest)
+            hRet = E_OUTOFMEMORY;
+        }
+        else
+          *lpDest = NULL;
       }
-      else if(psa->fFeatures & FADF_VARIANT) {
-        HRESULT hr = VariantCopy(elementStorageAddress, pv);
-        if (FAILED(hr)) {
-          SafeArrayUnlock(psa);
-          return hr;
+      else
+      {
+        if (psa->fFeatures & (FADF_UNKNOWN|FADF_DISPATCH))
+        {
+          LPUNKNOWN *lpUnknown = (LPUNKNOWN *)pvData;
+          LPUNKNOWN *lpDest = (LPUNKNOWN *)lpvDest;
+
+          if (*lpUnknown)
+            IUnknown_AddRef(*lpUnknown);
+          if (*lpDest)
+            IUnknown_Release(*lpDest);
         }
+        /* Copy the data over */
+        memcpy(lpvDest, pvData, psa->cbElements);
       }
-      else /* duplicate the memory */
-        memcpy(elementStorageAddress, pv, SafeArrayGetElemsize(psa) );
     }
-
-  } else {
-    ERR("SafeArray: Cannot lock array....\n");
-    return E_UNEXPECTED; /* UNDOC error condition */
+    SafeArrayUnlock(psa);
   }
-
-  TRACE("SafeArray: item put at address %p.\n",elementStorageAddress);
-  return SafeArrayUnlock(psa);
+  return hRet;
 }
 
 
 /*************************************************************************
  *		SafeArrayGetElement (OLEAUT32.25)
- * Return the data element corresponding the the given coordinate
+ *
+ * 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      *pv)
+HRESULT WINAPI SafeArrayGetElement(SAFEARRAY *psa, LONG *rgIndices, void *pvData)
 {
-  ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to
-                                         the desired one... */
-  PVOID elementStorageAddress = NULL; /* Address to store the data */
+  HRESULT hRet;
 
-  if(! validArg(psa))
+  TRACE("(%p,%p,%p)\n", psa, rgIndices, pvData);
+    
+  if (!psa || !rgIndices || !pvData)
     return E_INVALIDARG;
 
-  if(! validCoordinate(rgIndices, psa)) /* Validate the index given */
-    return(DISP_E_BADINDEX);
+  hRet = SafeArrayLock(psa);
 
-  if( SafeArrayLock(psa) == S_OK) {
+  if (SUCCEEDED(hRet))
+  {
+    PVOID lpvSrc;
 
-    /* Figure out the number of items to skip */
-    stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
+    hRet = SafeArrayPtrOfIndex(psa, rgIndices, &lpvSrc);
 
-    /* Figure out the number of byte to skip ... */
-    elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
+    if (SUCCEEDED(hRet))
+    {
+      if (psa->fFeatures & FADF_VARIANT)
+      {
+        VARIANT* lpVariant = (VARIANT*)lpvSrc;
+        VARIANT* lpDest = (VARIANT*)pvData;
 
-    if( psa->fFeatures & FADF_BSTR) {           /* reallocate the obj */
-      BSTR pbstrStoredStr = *(OLECHAR**)elementStorageAddress;
-      BSTR pbstrReturnedStr = NULL;
-      if( pbstrStoredStr &&
-          ((pbstrReturnedStr = SYSDUPSTRING( pbstrStoredStr )) == NULL) ) {
-        SafeArrayUnlock(psa);
-        return E_OUTOFMEMORY;
-      } else
-        *((BSTR*)pv) = pbstrReturnedStr;
-    }
-    else if( psa->fFeatures & FADF_VARIANT) {
-      HRESULT hr;
-      VariantInit(pv);
-      hr = VariantCopy(pv, elementStorageAddress);
-      if (FAILED(hr)) {
-        SafeArrayUnlock(psa);
-        return hr;
+        VariantCopy(lpDest, lpVariant);
       }
-    }
-    else if( isPointer(psa->fFeatures) )         /* simply copy the pointer */
-      *(PVOID*)pv = *((PVOID*)elementStorageAddress);
-    else                                         /* copy the bytes */
-      memcpy(pv, elementStorageAddress, psa->cbElements );
+      else if (psa->fFeatures & FADF_BSTR)
+      {
+        BSTR* lpBstr = (BSTR*)lpvSrc;
+        BSTR* lpDest = (BSTR*)pvData;
 
-  } else {
-    ERR("SafeArray: Cannot lock array....\n");
-    return E_UNEXPECTED; /* UNDOC error condition */
-  }
+        if (*lpBstr)
+        {
+          *lpDest = SysAllocStringLen(*lpBstr, SysStringLen(*lpBstr));
+          if (!*lpBstr)
+            hRet = E_OUTOFMEMORY;
+        }
+        else
+          *lpDest = NULL;
+      }
+      else
+      {
+        if (psa->fFeatures & (FADF_UNKNOWN|FADF_DISPATCH))
+        {
+          LPUNKNOWN *lpUnknown = (LPUNKNOWN *)lpvSrc;
 
-  return( SafeArrayUnlock(psa) );
+          if (*lpUnknown)
+            IUnknown_AddRef(*lpUnknown);
+        }
+        /* Copy the data over */
+        memcpy(pvData, lpvSrc, psa->cbElements);
+      }
+    }
+    SafeArrayUnlock(psa);
+  }
+  return hRet;
 }
 
 /*************************************************************************
  *		SafeArrayGetUBound (OLEAUT32.19)
- * return the UP bound for a given array dimension
- * Note: [0] is the right most (least significant) array index!
+ *
+ * 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)
+HRESULT WINAPI SafeArrayGetUBound(SAFEARRAY *psa, UINT nDim, LONG *plUbound)
 {
-  if(! validArg(psa))
+  TRACE("(%p,%d,%p)\n", psa, nDim, plUbound);
+    
+  if (!psa || !plUbound)
     return E_INVALIDARG;
 
-  if(nDim > psa->cDims)
-    return DISP_E_BADINDEX;
-
-  if(0 == nDim)
+  if(!nDim || nDim > psa->cDims || !psa->rgsabound[nDim - 1].cElements)
     return DISP_E_BADINDEX;
 
-  *plUbound = psa->rgsabound[psa->cDims - nDim].lLbound +
-              psa->rgsabound[psa->cDims - nDim].cElements - 1;
+  *plUbound = psa->rgsabound[nDim - 1].lLbound +
+              psa->rgsabound[nDim - 1].cElements - 1;
 
   return S_OK;
 }
 
 /*************************************************************************
  *		SafeArrayGetLBound (OLEAUT32.20)
- * Return the LO bound for a given array dimension
- * Note: [0] is the right most (least significant) array index!
+ *
+ * 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)
+HRESULT WINAPI SafeArrayGetLBound(SAFEARRAY *psa, UINT nDim, LONG *plLbound)
 {
-  if(! validArg(psa))
-    return E_INVALIDARG;
+  TRACE("(%p,%d,%p)\n", psa, nDim, plLbound);
 
-  if(nDim > psa->cDims)
-    return DISP_E_BADINDEX;
+  if (!psa || !plLbound)
+    return E_INVALIDARG;
 
-  if(0 == nDim)
+  if(!nDim || nDim > psa->cDims || !psa->rgsabound[nDim - 1].cElements)
     return DISP_E_BADINDEX;
 
-  *plLbound = psa->rgsabound[psa->cDims - nDim].lLbound;
+  *plLbound = psa->rgsabound[nDim - 1].lLbound;
   return S_OK;
 }
 
 /*************************************************************************
  *		SafeArrayGetDim (OLEAUT32.17)
- * returns the number of dimension in the array
+ *
+ * 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)
+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;
+  TRACE("(%p) returning %ld\n", psa, psa ? psa->cDims : 0ul);  
+  return psa ? psa->cDims : 0;
 }
 
 /*************************************************************************
  *		SafeArrayGetElemsize (OLEAUT32.18)
- * Return the size of the element in the array
+ *
+ * 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)
+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;
+  TRACE("(%p) returning %ld\n", psa, psa ? psa->cbElements : 0ul);
+  return psa ? psa->cbElements : 0;
 }
 
 /*************************************************************************
  *		SafeArrayAccessData (OLEAUT32.23)
- * increment the access count and return the data
+ *
+ * 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)
+HRESULT WINAPI SafeArrayAccessData(SAFEARRAY *psa, void **ppvData)
 {
-  HRESULT hRes;
+  TRACE("(%p,%p)\n", psa, ppvData);
 
-  if(! validArg(psa))
+  if(!psa || !ppvData)
     return E_INVALIDARG;
 
-  hRes = SafeArrayLock(psa);
-
-  switch (hRes) {
-    case S_OK:
-      (*ppvData) = psa->pvData;
-      break;
-    case E_INVALIDARG:
-      (*ppvData) = NULL;
-      return E_INVALIDARG;
+  if (SUCCEEDED(SafeArrayLock(psa)))
+  {
+    *ppvData = psa->pvData;
+    return S_OK;
   }
-
-  return S_OK;
+  *ppvData = NULL;
+  return E_UNEXPECTED;
 }
 
 
 /*************************************************************************
  *		SafeArrayUnaccessData (OLEAUT32.24)
- * Decrement the access count
+ *
+ * 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)
+HRESULT WINAPI SafeArrayUnaccessData(SAFEARRAY *psa)
 {
-  if(! validArg(psa))
-    return E_INVALIDARG;
-
-  return(SafeArrayUnlock(psa));
+  TRACE("(%p)\n", psa);
+  return SafeArrayUnlock(psa);
 }
 
 /************************************************************************
  *		SafeArrayPtrOfIndex (OLEAUT32.148)
- * Return a pointer to the element at rgIndices
+ *
+ * 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)
+HRESULT WINAPI SafeArrayPtrOfIndex(SAFEARRAY *psa, LONG *rgIndices, void **ppvData)
 {
-  ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to
-                                         the desired one... */
+  USHORT dim;
+  ULONG cell = 0, dimensionSize = 1;
+  SAFEARRAYBOUND* psab;
+  LONG c1;
 
-  if(! validArg(psa))
+  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;
 
-  if(! validCoordinate(rgIndices, psa))
-    return DISP_E_BADINDEX;
+  psab = psa->rgsabound;
+  c1 = *rgIndices++;
 
-  /* Although it is dangerous to do this without having a lock, it is not
-   * illegal.  Microsoft do warn of the danger.
-   */
+  if (c1 < psab->lLbound || c1 >= psab->lLbound + (LONG)psab->cElements)
+    return DISP_E_BADINDEX; /* Initial index out of bounds */
 
-  /* Figure out the number of items to skip */
-  stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
+  for (dim = 1; dim < psa->cDims; dim++)
+  {
+    dimensionSize *= psab->cElements;
 
-  *ppvData = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
+    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[0].lLbound);
 
+  *ppvData = (char*)psa->pvData + cell * psa->cbElements;
   return S_OK;
 }
 
 /************************************************************************
  *		SafeArrayDestroyData (OLEAUT32.39)
- * Frees the memory data bloc
+ *
+ * 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)
+HRESULT WINAPI SafeArrayDestroyData(SAFEARRAY *psa)
 {
-  HRESULT  hRes;
-  ULONG    ulWholeArraySize; /* count spot in array  */
-  ULONG    ulDataIter;       /* to iterate the data space */
-
-  if(! validArg(psa))
+  TRACE("(%p)\n", psa);
+  
+  if (!psa)
     return E_INVALIDARG;
 
-  if(psa->cLocks > 0)
-    return DISP_E_ARRAYISLOCKED;
-
-  if(psa->pvData==NULL)
-    return S_OK;
-
-  ulWholeArraySize = getArraySize(psa);
-
-  if(isPointer(psa->fFeatures)) {           /* release the pointers */
-    IUnknown *punk;
-
-    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 */
-    BSTR bstr;
-
-    for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
-      bstr = *(BSTR*)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));
+  if (psa->cLocks)
+    return DISP_E_ARRAYISLOCKED; /* Cant delete a locked array */
 
-      if( bstr != NULL)
-        SysFreeString( bstr );
-    }
-  }
-  else if(psa->fFeatures & FADF_VARIANT) {  /* deallocate the obj */
+  if (psa->pvData)
+  {
+    /* Delete the actual item data */
+    if (FAILED(SAFEARRAY_DestroyData(psa, 0)))
+      return E_UNEXPECTED;
 
-    for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
-      VariantClear((VARIANT*)((char *) psa->pvData+(ulDataIter*(psa->cbElements))));
+    /* 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;
     }
-  }
-
-  /* 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_CREATEVECTOR)) { /* Set when we do CreateVector */
-
-    /* free the whole chunk */
-    if((hRes = HeapFree( GetProcessHeap(), 0, psa->pvData)) == 0) /*failed*/
-      return E_UNEXPECTED; /* UNDOC error condition */
+    else
+      psa->fFeatures |= FADF_DATADELETED; /* Mark the data deleted */
 
-    psa->pvData = NULL;
   }
-
   return S_OK;
 }
 
 /************************************************************************
  *		SafeArrayCopyData (OLEAUT32.412)
- * Copy the psaSource's data block into psaTarget if dimension and size
- * permits it.
+ *
+ * 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)
+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;
+  int dim;
 
-  if(SafeArrayGetDim(psaSource) != SafeArrayGetDim(psaTarget))
+  TRACE("(%p,%p)\n", psaSource, psaTarget);
+  
+  if (!psaSource || !psaTarget ||
+      psaSource->cDims != psaTarget->cDims ||
+      psaSource->cbElements != psaTarget->cbElements)
     return E_INVALIDARG;
 
-  ulWholeArraySize = getArraySize(psaSource);
-
-  /* The two arrays boundaries must be of same length */
-  for(cDimCount=0;cDimCount < psaSource->cDims; cDimCount++)
-    if( psaSource->rgsabound[cDimCount].cElements !=
-      psaTarget->rgsabound[cDimCount].cElements)
+  /* 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( 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 );
-    }
-  }
-  else if( psaTarget->fFeatures & FADF_VARIANT) {
-
-    for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
-      VariantClear((VARIANT*)((char *) psaTarget->pvData + (lDelta * psaTarget->cbElements)));
-    }
-  }
-
-  return duplicateData(psaSource, psaTarget);
+  if (SUCCEEDED(SAFEARRAY_DestroyData(psaTarget, 0)) &&
+      SUCCEEDED(SAFEARRAY_CopyData(psaSource, psaTarget)))
+    return S_OK;
+  return E_UNEXPECTED;
 }
 
 /************************************************************************
  *		SafeArrayDestroy (OLEAUT32.16)
- * Deallocates all memory reserved for the SafeArray
+ *
+ * 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)
+HRESULT WINAPI SafeArrayDestroy(SAFEARRAY *psa)
 {
-  HRESULT hRes;
+  TRACE("(%p)\n", psa);
 
-  if(! validArg(psa))
+  if(!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 */
+  /* Native doesn't check to see if the free succeeds */
+  SafeArrayDestroyData(psa);
+  SafeArrayDestroyDescriptor(psa);
+  return S_OK;
 }
 
 /************************************************************************
  *		SafeArrayCopy (OLEAUT32.27)
- * Make a dupplicate of a SafeArray
+ *
+ * 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 WINAPI SafeArrayCopy(SAFEARRAY *psa, SAFEARRAY **ppsaOut)
 {
-  HRESULT hRes;
-  DWORD   dAllocSize;
-  ULONG   ulWholeArraySize; /* size of the thing */
-
-  if(! validArg(psa))
-    return E_INVALIDARG;
+  HRESULT hRet;
 
-  if((hRes=SafeArrayAllocDescriptor(psa->cDims, ppsaOut)) == S_OK){
+  TRACE("(%p,%p)\n", psa, ppsaOut);
 
-    /* Duplicate the SAFEARRAY struct */
-    memcpy(*ppsaOut, psa,
-            sizeof(*psa)+(sizeof(*(psa->rgsabound))*(psa->cDims-1)));
+  if (!ppsaOut)
+    return E_INVALIDARG;
 
-    /* If the features that use storage before the SAFEARRAY struct are
-     * enabled, also copy this memory range. Flags have been copied already.
-     */
-    if (psa->fFeatures & (FADF_HAVEIID | FADF_HAVEVARTYPE))
-      memcpy(((GUID*)*ppsaOut)-1, ((GUID*)psa)-1, sizeof(GUID));
+  *ppsaOut = NULL;
 
-    /* Copy the IRecordInfo* reference */
-    if (psa->fFeatures & FADF_RECORD) {
-      IRecordInfo *ri;
+  if (!psa)
+    return S_OK; /* Handles copying of NULL arrays */
 
-      ri = ((IRecordInfo**)psa)[-1];
-      if (ri) {
-	((IRecordInfo**)*ppsaOut)[-1] = ri;
-	IRecordInfo_AddRef(ri);
-      }
+  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;
     }
+  }
 
-    (*ppsaOut)->pvData = NULL; /* do not point to the same data area */
-
-    /* make sure the new safe array doesn't have the FADF_CREATEVECTOR flag,
-       because the data has not been allocated with the descriptor. */
-    (*ppsaOut)->fFeatures &= ~FADF_CREATEVECTOR;
-
-    /* Get the allocated memory size for source and allocate it for target */
-    ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
-    dAllocSize = ulWholeArraySize*psa->cbElements;
+  if (SUCCEEDED(hRet))
+  {
+    /* Copy dimension bounds */
+    memcpy((*ppsaOut)->rgsabound, psa->rgsabound, psa->cDims * sizeof(SAFEARRAYBOUND));
 
-    (*ppsaOut)->pvData =
-      HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dAllocSize);
-    if( (*ppsaOut)->pvData != NULL) {   /* HeapAlloc succeed */
+    (*ppsaOut)->pvData = SAFEARRAY_Malloc(SAFEARRAY_GetCellCount(psa) * psa->cbElements);
 
-      if( (hRes=duplicateData(psa, *ppsaOut)) != S_OK) { /* E_OUTOFMEMORY */
-        HeapFree(GetProcessHeap(), 0, (*ppsaOut)->pvData);
-        (*ppsaOut)->pvData = NULL;
-        SafeArrayDestroyDescriptor(*ppsaOut);
-        return hRes;
-      }
+    if ((*ppsaOut)->pvData)
+    {
+      hRet = SAFEARRAY_CopyData(psa, *ppsaOut);
+ 
+      if (SUCCEEDED(hRet))
+        return hRet;
 
-    } else { /* failed to allocate or dupplicate... */
-      SafeArrayDestroyDescriptor(*ppsaOut);
-      return E_UNEXPECTED; /* UNDOC error condition */
+      SAFEARRAY_Free((*ppsaOut)->pvData);
     }
-  } else { /* failed to allocate mem for descriptor */
-    return E_OUTOFMEMORY; /* UNDOC error condiftion */
+    SafeArrayDestroyDescriptor(*ppsaOut);
   }
-
-  return S_OK;
-}
-
-/************************************************************************
- *		SafeArrayCreateVector (OLEAUT32.411)
- * 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;
-  BYTE *ptr;
-
-  TRACE("%d, %ld, %ld\n", vt, lLbound, cElements);
-
-  /* Validate supported VARTYPE */
-  if ( (vt >= LAST_VARTYPE) ||
-       ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
-    return NULL;
-
-  /* Allocate memory for the array descriptor and data contiguously  */
-  ptr = HeapAlloc( GetProcessHeap(),
-                      HEAP_ZERO_MEMORY,
-                      (sizeof(GUID)+sizeof(*psa)+(VARTYPE_SIZE[vt]*cElements)));
-  if (!ptr)
-    return NULL;
-  psa = (SAFEARRAY*)(ptr+sizeof(GUID));
-
-  /* setup data members... */
-  psa->cDims      = 1; /* always and forever */
-  psa->fFeatures  = getFeatures(vt) | FADF_CREATEVECTOR;  /* undocumented flag used by Microsoft */
-  psa->cLocks     = 0;
-  psa->pvData     = (BYTE*)psa + sizeof(*psa);
-  psa->cbElements = VARTYPE_SIZE[vt];
-
-  psa->rgsabound[0].cElements = cElements;
-  psa->rgsabound[0].lLbound   = lLbound;
-
-  return(psa);
+  *ppsaOut = NULL;
+  return hRet;
 }
 
 /************************************************************************
  *		SafeArrayRedim (OLEAUT32.40)
- * Changes the caracteristics of the last dimension of the SafeArray
+ *
+ * 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 *psaboundNew)
+HRESULT WINAPI SafeArrayRedim(SAFEARRAY *psa, SAFEARRAYBOUND *psabound)
 {
-  LONG   lDelta;  /* hold difference in size */
-  USHORT cDims=1; /* dims counter */
+  SAFEARRAYBOUND *oldBounds;
 
-  if( !validArg(psa) )
+  TRACE("(%p,%p)\n", psa, psabound);
+  
+  if (!psa || psa->fFeatures & FADF_FIXEDSIZE || !psabound || !psabound->cElements)
     return E_INVALIDARG;
 
-  if( psa->cLocks > 0 )
+  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;
-
-  TRACE("elements=%ld, Lbound=%ld (delta=%ld)\n", psaboundNew->cElements, psaboundNew->lLbound, lDelta);
-
-  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
- ************************************************************************/
+  if (FAILED(SafeArrayLock(psa)))
+    return E_UNEXPECTED;
 
-/************************************************************************
- * Used to validate the SAFEARRAY type of arg
- */
-static BOOL validArg(
-  SAFEARRAY *psa)
-{
-  SAFEARRAYBOUND *sab;
-  LONG psaSize  = 0;
-  LONG descSize = 0;
-  LONG fullSize = 0;
+  oldBounds = &psa->rgsabound[psa->cDims - 1];
+  oldBounds->lLbound = psabound->lLbound;
 
-  /*
-   * Let's check for the null pointer just in case.
-   */
-  if (psa == NULL)
-    return FALSE;
+  if (psabound->cElements != oldBounds->cElements)
+  {
+    if (psabound->cElements < oldBounds->cElements)
+    {
+      /* Shorten the final dimension. */
+      ULONG ulStartCell = psa->cDims == 1 ? 0 : SAFEARRAY_GetDimensionCells(psa, psa->cDims - 1);
 
-  /* Check whether the size of the chunk makes sense... That's the only thing
-     I can think of now... */
+      ulStartCell += psabound->cElements;
+      SAFEARRAY_DestroyData(psa, ulStartCell);
+    }
+    else
+    {
+      /* Lengthen the final dimension */
+      ULONG ulOldSize, ulNewSize;
+      PVOID pvNewData;
 
-  psaSize = HeapSize(GetProcessHeap(), 0, ((IID*)psa)-1);
-  if (psaSize == -1)
-    /* uh, foreign heap. Better don't mess with it ! */
-    return TRUE;
+      ulOldSize = SAFEARRAY_GetCellCount(psa);
+      ulNewSize = (ulOldSize / oldBounds->cElements) * psabound->cElements;
 
-  /* size of the descriptor when the SA is not created with CreateVector */
-  descSize = sizeof(GUID) + sizeof(*psa) + (sizeof(*sab) * (psa->cDims-1));
+      if (ulOldSize == SAFEARRAY_INVALID_CELLS ||
+          !(pvNewData = SAFEARRAY_Malloc(ulNewSize)))
+      {
+        SafeArrayUnlock(psa);
+        return E_UNEXPECTED;
+      }
 
-  /* size of the descriptor + data when created with CreateVector */
-  fullSize = sizeof(*psa) + (psa->cbElements * psa->rgsabound[0].cElements);
+      memcpy(pvNewData, psa->pvData, ulOldSize);
+      SAFEARRAY_Free(psa->pvData);
+      psa->pvData = pvNewData;
+    }
+    oldBounds->cElements = psabound->cElements;
+  }
 
-  return((psaSize >= descSize) || (psaSize >= fullSize));
+  SafeArrayUnlock(psa);
+  return S_OK;
 }
 
 /************************************************************************
- * Used to reallocate memory
+ *		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.
  */
-static BOOL resizeSafeArray(
-  SAFEARRAY *psa,
-  LONG lDelta)
+HRESULT WINAPI SafeArrayGetVartype(SAFEARRAY* psa, VARTYPE* pvt)
 {
-  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));
+  TRACE("(%p,%p)\n", psa, pvt);
 
-        if( bstr != NULL )
-          SysFreeString( bstr );
-      }
-    else if(psa->fFeatures & FADF_VARIANT)
-      for(;lDelta < 0; lDelta++) {
-        VariantClear((VARIANT*)((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements)));
-      }
-  }
+  if (!psa || !pvt)
+    return E_INVALIDARG;
 
-  if (!(psa->fFeatures & FADF_CREATEVECTOR))
+  if (psa->fFeatures & FADF_RECORD)
+    *pvt = VT_RECORD;
+  else if (psa->fFeatures & FADF_HAVEIID)
+    *pvt = VT_UNKNOWN;
+  else if (psa->fFeatures & FADF_HAVEVARTYPE)
   {
-    /* 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(), HEAP_ZERO_MEMORY, 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
-                      */
+    VARTYPE vt = SAFEARRAY_GetHiddenDWORD(psa);
+    *pvt = vt;
   }
   else
-  {
-    /* Allocate a new block, because the previous data has been allocated with
-       the descriptor in SafeArrayCreateVector function. */
-
-    if((pvNewBlock = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
-       ulWholeArraySize * psa->cbElements)) == NULL)
-        return FALSE;
-
-    psa->fFeatures &= ~FADF_CREATEVECTOR;
-  }
-  /* 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_BSTR:      return FADF_BSTR;
-  case VT_UNKNOWN:   return FADF_UNKNOWN;
-  case VT_DISPATCH:  return FADF_DISPATCH;
-  case VT_VARIANT:   return FADF_VARIANT;
-  }
-  return 0;
-}
+    return E_INVALIDARG;
 
-/************************************************************************
- * 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;
+  return S_OK;
 }
 
 /************************************************************************
- * Used to calculate the displacement when accessing or modifying
- * safearray data set.
+ *		SafeArraySetRecordInfo (OLEAUT32.@)
  *
- *  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
+ * 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.
  */
-static ULONG calcDisplacement(
-  LONG           *coor,
-  SAFEARRAYBOUND *mat,
-  LONG           dim)
+HRESULT WINAPI SafeArraySetRecordInfo(SAFEARRAY *psa, IRecordInfo *pRinfo)
 {
-  ULONG res = 0;
-  LONG  iterDim;
+  IRecordInfo** dest = (IRecordInfo**)psa;
 
-  TRACE("dims is %ld\n", dim);
+  TRACE("(%p,%p)\n", psa, pRinfo);
+  
+  if (!psa || !(psa->fFeatures & FADF_RECORD))
+    return E_INVALIDARG;
 
-  for (iterDim = dim-1; iterDim >= 0; iterDim--) {
-	TRACE("%ld: lbound is %ld, adding %ld\n", iterDim, mat[dim-iterDim-1].lLbound,(coor[iterDim] - mat[dim-iterDim-1].lLbound));
-	res += (coor[iterDim] - mat[dim-iterDim-1].lLbound);
+  if (pRinfo)
+    IRecordInfo_AddRef(pRinfo);
 
-	if (iterDim > 0)
-		res *= mat[dim-iterDim].cElements;
-  }
+  if (dest[-1])
+    IRecordInfo_Release(dest[-1]);
 
-  TRACE("SafeArray: calculated displacement is %lu.\n", res);
-  return(res);
+  dest[-1] = pRinfo;
+  return S_OK;
 }
 
 /************************************************************************
- * Method used to validate the coordinate received in Put and Get
- * methods.
+ *		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.
  */
-static BOOL validCoordinate(
-  LONG      *coor,
-  SAFEARRAY *psa)
+HRESULT WINAPI SafeArrayGetRecordInfo(SAFEARRAY *psa, IRecordInfo **pRinfo)
 {
-  INT   iter=0;
-  LONG    lUBound;
-  LONG    lLBound;
-  HRESULT hRes;
+  IRecordInfo** src = (IRecordInfo**)psa;
 
-  if (!psa->cDims) { FIXME("no dims?\n");return FALSE; }
-  for(; iter<psa->cDims; iter++) {
-    TRACE("coor[%d]=%ld\n", iter, coor[iter]);
-    if((hRes = SafeArrayGetLBound(psa, (iter+1), &lLBound)) != S_OK) {
-      FIXME("No lbound?\n");
-      return FALSE;
-    }
-    if((hRes = SafeArrayGetUBound(psa, (iter+1), &lUBound)) != S_OK) {
-      FIXME("No ubound?\n");
-      return FALSE;
-    }
-    if(lLBound > lUBound) {
-      FIXME("lbound larger than ubound?\n");
-      return FALSE;
-    }
+  TRACE("(%p,%p)\n", psa, pRinfo);
 
-    if((coor[iter] < lLBound) || (coor[iter] > lUBound)) {
-      FIXME("coordinate %ld not within %ld - %ld\n",coor[iter], lLBound, lUBound);
-      return FALSE;
-    }
-  }
-  return TRUE;
+  if (!psa || !pRinfo || !(psa->fFeatures & FADF_RECORD))
+    return E_INVALIDARG;
+
+  *pRinfo = src[-1];
+
+  if (*pRinfo)
+    IRecordInfo_AddRef(*pRinfo);
+  return S_OK;
 }
 
 /************************************************************************
- * Method used to calculate the number of cells of the SA
+ *		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.
  */
-static ULONG getArraySize(
-  SAFEARRAY *psa)
+HRESULT WINAPI SafeArraySetIID(SAFEARRAY *psa, REFGUID guid)
 {
-  USHORT cCount;
-  ULONG  ulWholeArraySize = 1;
+  GUID* dest = (GUID*)psa;
 
-  for(cCount=0; cCount < psa->cDims; cCount++) /* foreach dimensions... */
-    ulWholeArraySize *= psa->rgsabound[cCount].cElements;
+  TRACE("(%p,%s)\n", psa, debugstr_guid(guid));
 
-  return ulWholeArraySize;
-}
+  if (!psa || !guid || !(psa->fFeatures & FADF_HAVEIID))
+    return E_INVALIDARG;
 
+  dest[-1] = *guid;
+  return S_OK;
+}
 
 /************************************************************************
- * Method used to handle data space dupplication for Copy32 and CopyData32
+ *		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.
  */
-static HRESULT duplicateData(
-  SAFEARRAY *psa,
-  SAFEARRAY *ppsaOut)
+HRESULT WINAPI SafeArrayGetIID(SAFEARRAY *psa, GUID *pGuid)
 {
-  ULONG    ulWholeArraySize; /* size of the thing */
-  LONG     lDelta;
-
-  ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
-
-  SafeArrayLock(ppsaOut);
-
-  if( isPointer(psa->fFeatures) ) {  /* If datatype is object increment
-                                        object's reference count */
-    IUnknown *punk;
-
-    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 */
-    BSTR   pbstrReAllocStr = NULL;
-
-    for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
-      if(( pbstrReAllocStr = SYSDUPSTRING(
-            *(BSTR*)((char *) psa->pvData+(lDelta * psa->cbElements)))) == NULL) {
-
-        SafeArrayUnlock(ppsaOut);
-        return E_OUTOFMEMORY;
-      }
-
-      *((BSTR*)((char *)ppsaOut->pvData+(lDelta * psa->cbElements))) =
-        pbstrReAllocStr;
-    }
+  GUID* src = (GUID*)psa;
 
-  }
-  else if( psa->fFeatures & FADF_VARIANT ) {
+  TRACE("(%p,%p)\n", psa, pGuid);
 
-    for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
-      VariantCopy((VARIANT*)((char *) ppsaOut->pvData+(lDelta * psa->cbElements)),
-                  (VARIANT*)((char *) psa->pvData+(lDelta * psa->cbElements)));
-    }
+  if (!psa || !pGuid || !(psa->fFeatures & FADF_HAVEIID))
+    return E_INVALIDARG;
 
-  } else { /* Simply copy the source array data into target array */
-    memcpy(ppsaOut->pvData, psa->pvData, ulWholeArraySize*psa->cbElements);
-  }
-  SafeArrayUnlock(ppsaOut);
+  *pGuid = src[-1];
   return S_OK;
 }
 
-
 /************************************************************************
- *		SafeArrayGetVartype (OLEAUT32.77)
- * Returns the VARTYPE stored in the given safearray
+ *		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 SafeArrayGetVartype(
-  SAFEARRAY* psa,
-  VARTYPE*   pvt)
+HRESULT WINAPI VectorFromBstr(BSTR bstr, SAFEARRAY **ppsa)
 {
-  if (psa->fFeatures & FADF_HAVEVARTYPE)
-  {
-    /* VT tag @ negative offset 4 in the array descriptor */
-    *pvt = ((DWORD*)psa)[-1];
-    return S_OK;
-  }
-
-  if (psa->fFeatures & FADF_RECORD)
-  {
-    *pvt = VT_RECORD;
-    return S_OK;
-  }
+  SAFEARRAYBOUND sab;
 
-  if (psa->fFeatures & FADF_BSTR)
-  {
-    *pvt = VT_BSTR;
-    return S_OK;
-  }
+  TRACE("(%p,%p)\n", bstr, ppsa);
+  
+  if (!ppsa)
+    return E_INVALIDARG;
 
-  if (psa->fFeatures & FADF_UNKNOWN)
-  {
-    *pvt = VT_UNKNOWN;
-    return S_OK;
-  }
+  sab.lLbound = 0;
+  sab.cElements = SysStringByteLen(bstr);
 
-  if (psa->fFeatures & FADF_DISPATCH)
-  {
-    *pvt = VT_UNKNOWN; /* Yes, checked against windows */
-    return S_OK;
-  }
+  *ppsa = SAFEARRAY_Create(VT_UI1, 1, &sab, 0);
 
-  if (psa->fFeatures & FADF_VARIANT)
-  {
-    *pvt = VT_VARIANT;
-    return S_OK;
-  }
-  if (psa->fFeatures & FADF_HAVEIID)
+  if (*ppsa)
   {
-    /* We could check the IID here, but Windows apparently does not
-     * do that and returns VT_UNKNOWN for VT_DISPATCH too.
-     */
-    *pvt = VT_UNKNOWN;
+    memcpy((*ppsa)->pvData, bstr, sab.cElements);
     return S_OK;
   }
-
-  WARN("No vt found for safearray\n");
-  return E_INVALIDARG;
-}
-
-/************************************************************************
- *		SafeArraySetIID (OLEAUT32.57)
- */
-HRESULT WINAPI SafeArraySetIID(SAFEARRAY *arr, REFIID riid) {
-  IID *xiid = ((IID*)arr)-1;
-  TRACE("(%p, %s).\n",arr,debugstr_guid(riid));
-
-  if (!arr || !(arr->fFeatures & FADF_HAVEIID))
-    return E_INVALIDARG;
-  memcpy(xiid, riid, sizeof(GUID));
-  return S_OK;
+  return E_OUTOFMEMORY;
 }
 
 /************************************************************************
- *		SafeArrayGetIID (OLEAUT32.67)
+ *		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 SafeArrayGetIID(SAFEARRAY *arr, IID *riid) {
-  IID *xiid = ((IID*)arr)-1;
-  TRACE("(%p, %s).\n",arr,debugstr_guid(riid));
+HRESULT WINAPI BstrFromVector(SAFEARRAY *psa, BSTR *pbstr)
+{
+  TRACE("(%p,%p)\n", psa, pbstr);
 
-  if (!arr || !(arr->fFeatures & FADF_HAVEIID))
+  if (!pbstr)
     return E_INVALIDARG;
-  memcpy(riid, xiid, sizeof(GUID));
-  return S_OK;
-}
 
-/************************************************************************
- *		SafeArraySetRecordInfo (OLEAUT32.44)
- */
-HRESULT WINAPI SafeArraySetRecordInfo(SAFEARRAY *arr, IRecordInfo *iface) {
-  LPRECORDINFO oldiface;
+  *pbstr = NULL;
 
-  if (!arr || !(arr->fFeatures & FADF_RECORD))
+  if (!psa || psa->cbElements != 1 || psa->cDims != 1)
     return E_INVALIDARG;
-  oldiface = ((IRecordInfo**)arr)[-1];
-  if (oldiface)
-    IRecordInfo_Release(oldiface);
-  ((IRecordInfo**)arr)[-1] = iface;
-  if (iface)
-    IRecordInfo_AddRef(iface);
-  return S_OK;
-}
 
-/************************************************************************
- *		SafeArrayGetRecordInfo (OLEAUT32.45)
- */
-HRESULT WINAPI SafeArrayGetRecordInfo(SAFEARRAY *arr, IRecordInfo** iface) {
-  if (!arr || !(arr->fFeatures & FADF_RECORD))
-    return E_INVALIDARG;
-  *iface = ((IRecordInfo**)arr)[-1];
-  if (*iface)
-    IRecordInfo_AddRef(*iface);
+  *pbstr = SysAllocStringByteLen(psa->pvData, psa->rgsabound[0].cElements);
+  if (!*pbstr)
+    return E_OUTOFMEMORY;
   return S_OK;
 }
diff -ur --minimal wine/dlls/oleaut32/tests/safearray.c wine-develop/dlls/oleaut32/tests/safearray.c
--- wine/dlls/oleaut32/tests/safearray.c	2003-09-11 16:11:10.000000000 +0000
+++ wine-develop/dlls/oleaut32/tests/safearray.c	2003-12-12 20:10:31.000000000 +0000
@@ -39,11 +39,147 @@
 #include "wtypes.h"
 #include "oleauto.h"
 
-static HRESULT (WINAPI *pSafeArrayAllocDescriptorEx)(VARTYPE,UINT,struct tagSAFEARRAY**)=NULL;
-static HRESULT (WINAPI *pSafeArrayCopyData)(struct tagSAFEARRAY*,struct tagSAFEARRAY*)=NULL;
-static HRESULT (WINAPI *pSafeArrayGetIID)(struct tagSAFEARRAY*,GUID*)=NULL;
-static HRESULT (WINAPI *pSafeArraySetIID)(struct tagSAFEARRAY*,REFGUID)=NULL;
-static HRESULT (WINAPI *pSafeArrayGetVartype)(struct tagSAFEARRAY*,VARTYPE*)=NULL;
+static HMODULE hOleaut32;
+
+static HRESULT (WINAPI *pSafeArrayAllocDescriptorEx)(VARTYPE,UINT,SAFEARRAY**);
+static HRESULT (WINAPI *pSafeArrayCopyData)(SAFEARRAY*,SAFEARRAY*);
+static HRESULT (WINAPI *pSafeArrayGetIID)(SAFEARRAY*,GUID*);
+static HRESULT (WINAPI *pSafeArraySetIID)(SAFEARRAY*,REFGUID);
+static HRESULT (WINAPI *pSafeArrayGetVartype)(SAFEARRAY*,VARTYPE*);
+static SAFEARRAY* (WINAPI *pSafeArrayCreateEx)(VARTYPE,UINT,SAFEARRAYBOUND*,LPVOID);
+
+#define GETPTR(func) p##func = (void*)GetProcAddress(hOleaut32, #func)
+
+/* Is a given function exported from oleaut32? */
+#define HAVE_FUNC(func) ((void*)GetProcAddress(hOleaut32, #func) != NULL)
+
+/* Have IRecordInfo data type? */
+#define HAVE_OLEAUT32_RECORD  HAVE_FUNC(SafeArraySetRecordInfo)
+/* Have I8/UI8 data type? */
+#define HAVE_OLEAUT32_I8      HAVE_FUNC(VarI8FromI1)
+
+#define START_REF_COUNT 1
+#define RECORD_SIZE 64
+#define RECORD_SIZE_FAIL 17
+/************************************************************************
+ * Dummy IRecordInfo Implementation
+ */
+typedef struct IRecordInfoImpl
+{
+  ICOM_VTABLE(IRecordInfo) *lpvtbl;
+  DWORD ref;
+  DWORD sizeCalled;
+  DWORD clearCalled;
+} IRecordInfoImpl;
+
+static ICOM_VTABLE(IRecordInfo) IRecordInfoImpl_VTable;
+
+static IRecordInfoImpl *IRecordInfoImpl_Construct()
+{
+  IRecordInfoImpl *rec;
+
+  rec = HeapAlloc(GetProcessHeap(), 0, sizeof(IRecordInfoImpl));
+  rec->lpvtbl = &IRecordInfoImpl_VTable;
+  rec->ref = START_REF_COUNT;
+  rec->clearCalled = 0;
+  rec->sizeCalled = 0;
+  return rec;
+}
+
+static ULONG CALLBACK IRecordInfoImpl_AddRef(IRecordInfo *iface)
+{
+  ICOM_THIS(IRecordInfoImpl, iface);
+  return ++This->ref;
+}
+
+static ULONG CALLBACK IRecordInfoImpl_Release(IRecordInfo *iface)
+{
+  ICOM_THIS(IRecordInfoImpl, iface);
+  return --This->ref;
+}
+
+static BOOL fail_GetSize; /* Whether to fail the GetSize call */
+
+static HRESULT CALLBACK IRecordInfoImpl_RecordClear(IRecordInfo *iface, PVOID pvExisting WINE_UNUSED)
+{
+  ICOM_THIS(IRecordInfoImpl, iface);
+  This->clearCalled++;
+  return S_OK;
+}
+
+static HRESULT CALLBACK IRecordInfoImpl_GetSize(IRecordInfo *iface, ULONG* size)
+{
+  ICOM_THIS(IRecordInfoImpl, iface);
+  This->sizeCalled++;
+  *size = 17;
+  if (fail_GetSize)
+    return E_UNEXPECTED;
+  *size = RECORD_SIZE;
+  return S_OK;
+}
+
+static HRESULT CALLBACK IRecordInfoImpl_Dummy(IRecordInfo *iface WINE_UNUSED)
+{
+  trace("Called an unexpected IRecordInfo method - please report!\n");
+  /* Quit because we'll just crash anyway */
+  fflush(NULL);
+  exit(255);
+}
+
+static ICOM_VTABLE(IRecordInfo) IRecordInfoImpl_VTable =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+  (PVOID)IRecordInfoImpl_Dummy,
+  IRecordInfoImpl_AddRef,
+  IRecordInfoImpl_Release,
+  (PVOID)IRecordInfoImpl_Dummy,
+  IRecordInfoImpl_RecordClear,
+  (PVOID)IRecordInfoImpl_Dummy,
+  (PVOID)IRecordInfoImpl_Dummy,
+  (PVOID)IRecordInfoImpl_Dummy,
+  (PVOID)IRecordInfoImpl_GetSize,
+  (PVOID)IRecordInfoImpl_Dummy,
+  (PVOID)IRecordInfoImpl_Dummy,
+  (PVOID)IRecordInfoImpl_Dummy,
+  (PVOID)IRecordInfoImpl_Dummy,
+  (PVOID)IRecordInfoImpl_Dummy,
+  (PVOID)IRecordInfoImpl_Dummy,
+  (PVOID)IRecordInfoImpl_Dummy,
+  (PVOID)IRecordInfoImpl_Dummy,
+  (PVOID)IRecordInfoImpl_Dummy,
+  (PVOID)IRecordInfoImpl_Dummy
+};
+
+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);
+  }
+  return 0;
+}
 
 #define VARTYPE_NOT_SUPPORTED 0
 static struct {
@@ -96,9 +232,8 @@
 {VT_CLSID,    VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0},
 };
 
-START_TEST(safearray)
+static void test_safearray(void)
 {
-	HMODULE hdll;
 	SAFEARRAY 	*a, b, *c;
 	unsigned int 	i;
 	long		indices[2];
@@ -111,138 +246,110 @@
 	LONG		l;
 	unsigned char	*ptr1, *ptr2;
 
-    hdll=LoadLibraryA("oleaut32.dll");
-    pSafeArrayAllocDescriptorEx=(void*)GetProcAddress(hdll,"SafeArrayAllocDescriptorEx");
-    pSafeArrayCopyData=(void*)GetProcAddress(hdll,"SafeArrayCopyData");
-    pSafeArrayGetIID=(void*)GetProcAddress(hdll,"SafeArrayGetIID");
-    pSafeArraySetIID=(void*)GetProcAddress(hdll,"SafeArraySetIID");
-    pSafeArrayGetVartype=(void*)GetProcAddress(hdll,"SafeArrayGetVartype");
-
-	hres = SafeArrayAllocDescriptor(0,&a);
-	ok(E_INVALIDARG == hres,"SAAD(0) failed with hres %lx",hres);
-
-	hres=SafeArrayAllocDescriptor(1,&a);
-	ok(S_OK == hres,"SAAD(1) failed with %lx",hres);
-
-	for (i=1;i<100;i++) {
-		hres=SafeArrayAllocDescriptor(i,&a);
-		ok(S_OK == hres,"SAAD(%d) failed with %lx",i,hres);
-		
-		ok(a->cDims == i,"a->cDims not initialised?");
-
-		hres=SafeArrayDestroyDescriptor(a);
-		ok(S_OK == hres,"SADD failed with %lx",hres);
-	}
-
-	hres=SafeArrayAllocDescriptor(65535,&a);
-	ok(S_OK == hres,"SAAD(65535) failed with %lx",hres);
-
-	hres=SafeArrayDestroyDescriptor(a);
-	ok(S_OK == hres,"SADD failed with %lx",hres);
-
-	hres=SafeArrayAllocDescriptor(65536,&a);
-	ok(E_INVALIDARG == hres,"SAAD(65536) failed with %lx",hres);
-
-	/* Crashes on Win95: SafeArrayAllocDescriptor(xxx,NULL) */
-	
 	bound.cElements	= 1;
 	bound.lLbound	= 0;
 	a = SafeArrayCreate(-1, 1, &bound);
-	ok(NULL == a,"SAC(-1,1,[1,0]) not failed?");
+	ok(NULL == a,"SAC(-1,1,[1,0]) not failed?\n");
 
 
 	bounds[0].cElements = 42;	bounds[0].lLbound =  1;
 	bounds[1].cElements =  2;	bounds[1].lLbound = 23;
     a = SafeArrayCreate(VT_I4,2,bounds);
-    ok(a != NULL,"SAC(VT_INT32,2,...) failed.");
+    ok(a != NULL,"SAC(VT_INT32,2,...) failed.\n");
 
 	hres = SafeArrayGetLBound (a, 0, &l);
-	ok (hres == DISP_E_BADINDEX, "SAGLB 0 failed with %lx", hres);
+	ok (hres == DISP_E_BADINDEX, "SAGLB 0 failed with %lx\n", hres);
 	hres = SafeArrayGetLBound (a, 1, &l);
-	ok (hres == S_OK, "SAGLB 1 failed with %lx", hres);
-	ok (l == 1, "SAGLB 1 returned %ld instead of 1", l);
+	ok (hres == S_OK, "SAGLB 1 failed with %lx\n", hres);
+	ok (l == 1, "SAGLB 1 returned %ld instead of 1\n", l);
 	hres = SafeArrayGetLBound (a, 2, &l);
-	ok (hres == S_OK, "SAGLB 2 failed with %lx", hres);
-	ok (l == 23, "SAGLB 2 returned %ld instead of 1", l);
+	ok (hres == S_OK, "SAGLB 2 failed with %lx\n", hres);
+	ok (l == 23, "SAGLB 2 returned %ld instead of 23\n", l);
 	hres = SafeArrayGetLBound (a, 3, &l);
-	ok (hres == DISP_E_BADINDEX, "SAGLB 3 failed with %lx", hres);
+	ok (hres == DISP_E_BADINDEX, "SAGLB 3 failed with %lx\n", hres);
 
 	hres = SafeArrayGetUBound (a, 0, &l);
-	ok (hres == DISP_E_BADINDEX, "SAGUB 0 failed with %lx", hres);
+	ok (hres == DISP_E_BADINDEX, "SAGUB 0 failed with %lx\n", hres);
 	hres = SafeArrayGetUBound (a, 1, &l);
-	ok (hres == S_OK, "SAGUB 1 failed with %lx", hres);
-	ok (l == 42, "SAGUB 1 returned %ld instead of 1", l);
+	ok (hres == S_OK, "SAGUB 1 failed with %lx\n", hres);
+	ok (l == 42, "SAGUB 1 returned %ld instead of 42\n", l);
 	hres = SafeArrayGetUBound (a, 2, &l);
-	ok (hres == S_OK, "SAGUB 2 failed with %lx", hres);
-	ok (l == 24, "SAGUB 2 returned %ld instead of 24", l);
+	ok (hres == S_OK, "SAGUB 2 failed with %lx\n", hres);
+	ok (l == 24, "SAGUB 2 returned %ld instead of 24\n", l);
 	hres = SafeArrayGetUBound (a, 3, &l);
-	ok (hres == DISP_E_BADINDEX, "SAGUB 3 failed with %lx", hres);
+	ok (hres == DISP_E_BADINDEX, "SAGUB 3 failed with %lx\n", hres);
 
 	i = SafeArrayGetDim(a);
-	ok(i == 2, "getdims of 2 din array returned %d",i);
+	ok(i == 2, "getdims of 2 din array returned %d\n",i);
 
 	indices[0] = 0;
 	indices[1] = 23;
 	hres = SafeArrayGetElement(a, indices, &i);
-	ok(DISP_E_BADINDEX == hres,"SAGE failed [0,23], hres 0x%lx",hres);
+	ok(DISP_E_BADINDEX == hres,"SAGE failed [0,23], hres 0x%lx\n",hres);
 
 	indices[0] = 1;
 	indices[1] = 22;
 	hres = SafeArrayGetElement(a, indices, &i);
-	ok(DISP_E_BADINDEX == hres,"SAGE failed [1,22], hres 0x%lx",hres);
+	ok(DISP_E_BADINDEX == hres,"SAGE failed [1,22], hres 0x%lx\n",hres);
 
 	indices[0] = 1;
 	indices[1] = 23;
 	hres = SafeArrayGetElement(a, indices, &i);
-	ok(S_OK == hres,"SAGE failed [1,23], hres 0x%lx",hres);
+	ok(S_OK == hres,"SAGE failed [1,23], hres 0x%lx\n",hres);
 
 	indices[0] = 1;
 	indices[1] = 25;
 	hres = SafeArrayGetElement(a, indices, &i);
-	ok(DISP_E_BADINDEX == hres,"SAGE failed [1,24], hres 0x%lx",hres);
+	ok(DISP_E_BADINDEX == hres,"SAGE failed [1,24], hres 0x%lx\n",hres);
 
 	indices[0] = 3;
 	indices[1] = 23;
 	hres = SafeArrayGetElement(a, indices, &i);
-	ok(S_OK == hres,"SAGE failed [42,23], hres 0x%lx",hres);
+	ok(S_OK == hres,"SAGE failed [42,23], hres 0x%lx\n",hres);
 
 	hres = SafeArrayAccessData(a, (void**)&ptr1);
-	ok(S_OK == hres, "SAAD failed with 0x%lx", hres);
+	ok(S_OK == hres, "SAAD failed with 0x%lx\n", hres);
 
 	indices[0] = 3;
 	indices[1] = 23;
 	hres = SafeArrayPtrOfIndex(a, indices, (void**)&ptr2);
-	ok(S_OK == hres,"SAPOI failed [1,23], hres 0x%lx",hres);
-	ok(ptr2 - ptr1 == 8,"ptr difference is not 8, but %d (%p vs %p)", ptr2-ptr1, ptr2, ptr1);
+	ok(S_OK == hres,"SAPOI failed [1,23], hres 0x%lx\n",hres);
+	ok(ptr2 - ptr1 == 8,"ptr difference is not 8, but %d (%p vs %p)\n", ptr2-ptr1, ptr2, ptr1);
 
 	indices[0] = 3;
 	indices[1] = 24;
 	hres = SafeArrayPtrOfIndex(a, indices, (void**)&ptr2);
-	ok(S_OK == hres,"SAPOI failed [5,24], hres 0x%lx",hres);
-	ok(ptr2 - ptr1 == 176,"ptr difference is not 176, but %d (%p vs %p)", ptr2-ptr1, ptr2, ptr1);
+	ok(S_OK == hres,"SAPOI failed [5,24], hres 0x%lx\n",hres);
+	ok(ptr2 - ptr1 == 176,"ptr difference is not 176, but %d (%p vs %p)\n", ptr2-ptr1, ptr2, ptr1);
 
 	indices[0] = 20;
 	indices[1] = 23;
 	hres = SafeArrayPtrOfIndex(a, indices, (void**)&ptr2);
-	ok(S_OK == hres,"SAPOI failed [20,23], hres 0x%lx",hres);
-	ok(ptr2 - ptr1 == 76,"ptr difference is not 176, but %d (%p vs %p)", ptr2-ptr1, ptr2, ptr1);
+	ok(S_OK == hres,"SAPOI failed [20,23], hres 0x%lx\n",hres);
+	ok(ptr2 - ptr1 == 76,"ptr difference is not 176, but %d (%p vs %p)\n", ptr2-ptr1, ptr2, ptr1);
 
 	hres = SafeArrayUnaccessData(a);
-	ok(S_OK == hres, "SAUAD failed with 0x%lx", hres);
+	ok(S_OK == hres, "SAUAD failed with 0x%lx\n", hres);
 
 	for (i=0;i<sizeof(vttypes)/sizeof(vttypes[0]);i++) {
+        if ((i == VT_I8 || i == VT_UI8) && HAVE_OLEAUT32_I8)
+        {
+            vttypes[i].elemsize = sizeof(LONG64);
+        }
+
 		a = SafeArrayCreate(vttypes[i].vt, 1, &bound);
 		ok(	((a == NULL) && (vttypes[i].elemsize == 0)) ||
 			((a != NULL) && (vttypes[i].elemsize == a->cbElements)),
-		"SAC(%d,1,[1,0]), result %ld, expected %d",vttypes[i].vt,(a?a->cbElements:0),vttypes[i].elemsize
+		"SAC(%d,1,[1,0]), result %ld, expected %d\n",
+                vttypes[i].vt,(a?a->cbElements:0),vttypes[i].elemsize
 		);
         if (a!=NULL) {
 			ok(a->fFeatures == (vttypes[i].expflags | vttypes[i].addflags),
-               "SAC of %d returned feature flags %x, expected %x",
+               "SAC of %d returned feature flags %x, expected %x\n",
                vttypes[i].vt, a->fFeatures,
                vttypes[i].expflags|vttypes[i].addflags);
     		ok(SafeArrayGetElemsize(a) == vttypes[i].elemsize,
-               "SAGE for vt %d returned elemsize %d instead of expected %d",
+               "SAGE for vt %d returned elemsize %d instead of expected %d\n",
                vttypes[i].vt, SafeArrayGetElemsize(a),vttypes[i].elemsize);
         }
 
@@ -250,88 +357,88 @@
 
         if (pSafeArrayGetVartype) {
             hres = pSafeArrayGetVartype(a, &vt);
-            ok(hres == S_OK, "SAGVT of arra y with vt %d failed with %lx", vttypes[i].vt, hres);
+            ok(hres == S_OK, "SAGVT of arra y with vt %d failed with %lx\n", vttypes[i].vt, hres);
             if (vttypes[i].vt == VT_DISPATCH) {
         		/* Special case. Checked against Windows. */
-		        ok(vt == VT_UNKNOWN, "SAGVT of a        rray with VT_DISPATCH returned not VT_UNKNOWN, but %d", vt);
+		        ok(vt == VT_UNKNOWN, "SAGVT of a        rray with VT_DISPATCH returned not VT_UNKNOWN, but %d\n", vt);
             } else {
-		        ok(vt == vttypes[i].vt, "SAGVT of array with vt %d returned %d", vttypes[i].vt, vt);
+		        ok(vt == vttypes[i].vt, "SAGVT of array with vt %d returned %d\n", vttypes[i].vt, vt);
             }
         }
 
 		hres = SafeArrayCopy(a, &c);
-		ok(hres == S_OK, "failed to copy safearray of vt %d with hres %lx", vttypes[i].vt, hres);
+		ok(hres == S_OK, "failed to copy safearray of vt %d with hres %lx\n", vttypes[i].vt, hres);
 
-		ok(vttypes[i].elemsize == c->cbElements,"copy of SAC(%d,1,[1,0]), result %ld, expected %d",vttypes[i].vt,(c?c->cbElements:0),vttypes[i].elemsize
+		ok(vttypes[i].elemsize == c->cbElements,"copy of SAC(%d,1,[1,0]), result %ld, expected %d\n",vttypes[i].vt,(c?c->cbElements:0),vttypes[i].elemsize
 		);
-		ok(c->fFeatures == (vttypes[i].expflags | vttypes[i].addflags),"SAC of %d returned feature flags %x, expected %x", vttypes[i].vt, c->fFeatures, vttypes[i].expflags|vttypes[i].addflags);
-		ok(SafeArrayGetElemsize(c) == vttypes[i].elemsize,"SAGE for vt %d returned elemsize %d instead of expected %d",vttypes[i].vt, SafeArrayGetElemsize(c),vttypes[i].elemsize);
+		ok(c->fFeatures == (vttypes[i].expflags | vttypes[i].addflags),"SAC of %d returned feature flags %x, expected %x\n", vttypes[i].vt, c->fFeatures, vttypes[i].expflags|vttypes[i].addflags);
+		ok(SafeArrayGetElemsize(c) == vttypes[i].elemsize,"SAGE for vt %d returned elemsize %d instead of expected %d\n",vttypes[i].vt, SafeArrayGetElemsize(c),vttypes[i].elemsize);
 
         if (pSafeArrayGetVartype) {
             hres = pSafeArrayGetVartype(c, &vt);
-            ok(hres == S_OK, "SAGVT of array with vt %d failed with %lx", vttypes[i].vt, hres);
+            ok(hres == S_OK, "SAGVT of array with vt %d failed with %lx\n", vttypes[i].vt, hres);
             if (vttypes[i].vt == VT_DISPATCH) {
                 /* Special case. Checked against Windows. */
-                ok(vt == VT_UNKNOWN, "SAGVT of array with VT_DISPATCH returned not VT_UNKNOWN, but %d", vt);
+                ok(vt == VT_UNKNOWN, "SAGVT of array with VT_DISPATCH returned not VT_UNKNOWN, but %d\n", vt);
             } else {
-                ok(vt == vttypes[i].vt, "SAGVT of array with vt %d returned %d", vttypes[i].vt, vt);
+                ok(vt == vttypes[i].vt, "SAGVT of array with vt %d returned %d\n", vttypes[i].vt, vt);
             }
         }
 
         if (pSafeArrayCopyData) {
             hres = pSafeArrayCopyData(a, c);
-            ok(hres == S_OK, "failed to copy safearray data of vt %d with hres %lx", vttypes[i].vt, hres);
+            ok(hres == S_OK, "failed to copy safearray data of vt %d with hres %lx\n", vttypes[i].vt, hres);
 
             hres = SafeArrayDestroyData(c);
-            ok(hres == S_OK,"SADD of copy of array with vt %d failed with hres %lx", vttypes[i].vt, hres);
+            ok(hres == S_OK,"SADD of copy of array with vt %d failed with hres %lx\n", vttypes[i].vt, hres);
         }
 
 		hres = SafeArrayDestroy(a);
-		ok(hres == S_OK,"SAD of array with vt %d failed with hres %lx", vttypes[i].vt, hres);
+		ok(hres == S_OK,"SAD of array with vt %d failed with hres %lx\n", vttypes[i].vt, hres);
 	}
 
 	/* Test conversion of type|VT_ARRAY <-> VT_BSTR */
 	bound.lLbound = 0;
 	bound.cElements = 10;
 	a = SafeArrayCreate(VT_UI1, 1, &bound);
-	ok(a != NULL, "SAC failed.");
-	ok(S_OK == SafeArrayAccessData(a, &data),"SACD failed");
-	memcpy(data,"Hello World",10);
-	ok(S_OK == SafeArrayUnaccessData(a),"SAUD failed");
+	ok(a != NULL, "SAC failed.\n");
+	ok(S_OK == SafeArrayAccessData(a, &data),"SACD failed\n");
+	memcpy(data,"Hello World\n",10);
+	ok(S_OK == SafeArrayUnaccessData(a),"SAUD failed\n");
 	V_VT(&v) = VT_ARRAY|VT_UI1;
 	V_ARRAY(&v) = a;
 	hres = VariantChangeTypeEx(&v, &v, 0, 0, VT_BSTR);
-	ok(hres==S_OK, "CTE VT_ARRAY|VT_UI1 -> VT_BSTR failed with %lx",hres);
-	ok(V_VT(&v) == VT_BSTR,"CTE VT_ARRAY|VT_UI1 -> VT_BSTR did not return VT_BSTR, but %d.",V_VT(&v));
-	ok(V_BSTR(&v)[0] == 0x6548,"First letter are not 'He', but %x", V_BSTR(&v)[0]);
+	ok(hres==S_OK, "CTE VT_ARRAY|VT_UI1 -> VT_BSTR failed with %lx\n",hres);
+	ok(V_VT(&v) == VT_BSTR,"CTE VT_ARRAY|VT_UI1 -> VT_BSTR did not return VT_BSTR, but %d.v\n",V_VT(&v));
+	ok(V_BSTR(&v)[0] == 0x6548,"First letter are not 'He', but %x\n", V_BSTR(&v)[0]);
 
 	/* check locking functions */
 	a = SafeArrayCreate(VT_I4, 1, &bound);
-	ok(a!=NULL,"SAC should not fail");
+	ok(a!=NULL,"SAC should not fail\n");
 
 	hres = SafeArrayAccessData(a, &data);
-	ok(hres == S_OK,"SAAD failed with hres %lx",hres);
+	ok(hres == S_OK,"SAAD failed with hres %lx\n",hres);
 
 	hres = SafeArrayDestroy(a);
-	ok(hres == DISP_E_ARRAYISLOCKED,"locked safe array destroy not failed with DISP_E_ARRAYISLOCKED, but with hres %lx", hres);
+	ok(hres == DISP_E_ARRAYISLOCKED,"locked safe array destroy not failed with DISP_E_ARRAYISLOCKED, but with hres %lx\n", hres);
 
 	hres = SafeArrayDestroyData(a);
-	ok(hres == DISP_E_ARRAYISLOCKED,"locked safe array destroy data not failed with DISP_E_ARRAYISLOCKED, but with hres %lx", hres);
+	ok(hres == DISP_E_ARRAYISLOCKED,"locked safe array destroy data not failed with DISP_E_ARRAYISLOCKED, but with hres %lx\n", hres);
 
 	hres = SafeArrayDestroyDescriptor(a);
-	ok(hres == DISP_E_ARRAYISLOCKED,"locked safe array destroy descriptor not failed with DISP_E_ARRAYISLOCKED, but with hres %lx", hres);
+	ok(hres == DISP_E_ARRAYISLOCKED,"locked safe array destroy descriptor not failed with DISP_E_ARRAYISLOCKED, but with hres %lx\n", hres);
 
 	hres = SafeArrayUnaccessData(a);
-	ok(hres == S_OK,"SAUD failed after lock/destroy test");
+	ok(hres == S_OK,"SAUD failed after lock/destroy test\n");
 
 	hres = SafeArrayDestroy(a);
-	ok(hres == S_OK,"SAD failed after lock/destroy test");
+	ok(hres == S_OK,"SAD failed after lock/destroy test\n");
 
 	/* Test if we need to destroy data before descriptor */
 	a = SafeArrayCreate(VT_I4, 1, &bound);
-	ok(a!=NULL,"SAC should not fail");
+	ok(a!=NULL,"SAC should not fail\n");
 	hres = SafeArrayDestroyDescriptor(a);
-	ok(hres == S_OK,"SADD with data in array failed with hres %lx",hres);
+	ok(hres == S_OK,"SADD with data in array failed with hres %lx\n",hres);
 
 
 	/* IID functions */
@@ -341,68 +448,902 @@
         b.cDims = 1;
         memset(&iid, 0x42, sizeof(IID));
         hres = pSafeArraySetIID(&b,&iid);
-        ok(hres == E_INVALIDARG,"SafeArraySetIID of non IID capable safearray did not return E_INVALIDARG, but %lx",hres);
+        ok(hres == E_INVALIDARG,"SafeArraySetIID of non IID capable safearray did not return E_INVALIDARG, but %lx\n",hres);
 
         hres = SafeArrayAllocDescriptor(1,&a);
-        ok((a->fFeatures & FADF_HAVEIID) == 0,"newly allocated descriptor with SAAD should not have FADF_HAVEIID");
+        ok((a->fFeatures & FADF_HAVEIID) == 0,"newly allocated descriptor with SAAD should not have FADF_HAVEIID\n");
         hres = pSafeArraySetIID(a,&iid);
-        ok(hres == E_INVALIDARG,"SafeArraySetIID of newly allocated descriptor with SAAD should return E_INVALIDARG, but %lx",hres);
+        ok(hres == E_INVALIDARG,"SafeArraySetIID of newly allocated descriptor with SAAD should return E_INVALIDARG, but %lx\n",hres);
     }
 
     if (!pSafeArrayAllocDescriptorEx)
         return;
 
 	for (i=0;i<sizeof(vttypes)/sizeof(vttypes[0]);i++) {
+        a = NULL;
 		hres = pSafeArrayAllocDescriptorEx(vttypes[i].vt,1,&a);
 		ok(a->fFeatures == vttypes[i].expflags,"SAADE(%d) resulted with flags %x, expected %x\n", vttypes[i].vt, a->fFeatures, vttypes[i].expflags);
 		if (a->fFeatures & FADF_HAVEIID) {
 			hres = pSafeArrayGetIID(a, &iid);
-			ok(hres == S_OK,"SAGIID failed for vt %d with hres %lx", vttypes[i].vt,hres);
+			ok(hres == S_OK,"SAGIID failed for vt %d with hres %lx\n", vttypes[i].vt,hres);
 			switch (vttypes[i].vt) {
 			case VT_UNKNOWN:
-				ok(IsEqualGUID(((GUID*)a)-1,&IID_IUnknown),"guid for VT_UNKNOWN is not IID_IUnknown");
-				ok(IsEqualGUID(&iid, &IID_IUnknown),"SAGIID returned wrong GUID for IUnknown");
+				ok(IsEqualGUID(((GUID*)a)-1,&IID_IUnknown),"guid for VT_UNKNOWN is not IID_IUnknown\n");
+				ok(IsEqualGUID(&iid, &IID_IUnknown),"SAGIID returned wrong GUID for IUnknown\n");
 				break;
 			case VT_DISPATCH:
-				ok(IsEqualGUID(((GUID*)a)-1,&IID_IDispatch),"guid for VT_UNKNOWN is not IID_IDispatch");
-				ok(IsEqualGUID(&iid, &IID_IDispatch),"SAGIID returned wrong GUID for IDispatch");
+				ok(IsEqualGUID(((GUID*)a)-1,&IID_IDispatch),"guid for VT_UNKNOWN is not IID_IDispatch\n");
+				ok(IsEqualGUID(&iid, &IID_IDispatch),"SAGIID returned wrong GUID for IDispatch\n");
 				break;
 			default:
-				ok(FALSE,"unknown vt %d with FADF_HAVEIID",vttypes[i].vt);
+				ok(FALSE,"unknown vt %d with FADF_HAVEIID\n",vttypes[i].vt);
 				break;
 			}
 		} else {
 			hres = pSafeArrayGetIID(a, &iid);
-			ok(hres == E_INVALIDARG,"SAGIID did not fail for vt %d with hres %lx", vttypes[i].vt,hres);
+			ok(hres == E_INVALIDARG,"SAGIID did not fail for vt %d with hres %lx\n", vttypes[i].vt,hres);
 		}
 		if (a->fFeatures & FADF_RECORD) {
-			ok(vttypes[i].vt == VT_RECORD,"FADF_RECORD for non record %d",vttypes[i].vt);
+			ok(vttypes[i].vt == VT_RECORD,"FADF_RECORD for non record %d\n",vttypes[i].vt);
 		}
 		if (a->fFeatures & FADF_HAVEVARTYPE) {
-			ok(vttypes[i].vt == ((DWORD*)a)[-1], "FADF_HAVEVARTYPE set, but vt %d mismatch stored %ld",vttypes[i].vt,((DWORD*)a)[-1]);
+			ok(vttypes[i].vt == ((DWORD*)a)[-1], "FADF_HAVEVARTYPE set, but vt %d mismatch stored %ld\n",vttypes[i].vt,((DWORD*)a)[-1]);
 		}
 
 		hres = pSafeArrayGetVartype(a, &vt);
-		ok(hres == S_OK, "SAGVT of array with vt %d failed with %lx", vttypes[i].vt, hres);
+		ok(hres == S_OK, "SAGVT of array with vt %d failed with %lx\n", vttypes[i].vt, hres);
 
 		if (vttypes[i].vt == VT_DISPATCH) {
 			/* Special case. Checked against Windows. */
-			ok(vt == VT_UNKNOWN, "SAGVT of array with VT_DISPATCH returned not VT_UNKNOWN, but %d", vt);
+			ok(vt == VT_UNKNOWN, "SAGVT of array with VT_DISPATCH returned not VT_UNKNOWN, but %d\n", vt);
 		} else {
-			ok(vt == vttypes[i].vt, "SAGVT of array with vt %d returned %d", vttypes[i].vt, vt);
+			ok(vt == vttypes[i].vt, "SAGVT of array with vt %d returned %d\n", vttypes[i].vt, vt);
 		}
 
 		if (a->fFeatures & FADF_HAVEIID) {
 			hres = pSafeArraySetIID(a, &IID_IStorage); /* random IID */
-			ok(hres == S_OK,"SASIID failed with FADF_HAVEIID set for vt %d with %lx", vttypes[i].vt, hres);
+			ok(hres == S_OK,"SASIID failed with FADF_HAVEIID set for vt %d with %lx\n", vttypes[i].vt, hres);
 			hres = pSafeArrayGetIID(a, &iid);
-			ok(hres == S_OK,"SAGIID failed with FADF_HAVEIID set for vt %d with %lx", vttypes[i].vt, hres);
-			ok(IsEqualGUID(&iid, &IID_IStorage),"returned iid is not IID_IStorage");
+			ok(hres == S_OK,"SAGIID failed with FADF_HAVEIID set for vt %d with %lx\n", vttypes[i].vt, hres);
+			ok(IsEqualGUID(&iid, &IID_IStorage),"returned iid is not IID_IStorage\n");
 		} else {
 			hres = pSafeArraySetIID(a, &IID_IStorage); /* random IID */
-			ok(hres == E_INVALIDARG,"SASIID did not failed with !FADF_HAVEIID set for vt %d with %lx", vttypes[i].vt, hres);
+			ok(hres == E_INVALIDARG,"SASIID did not failed with !FADF_HAVEIID set for vt %d with %lx\n", vttypes[i].vt, hres);
 		}
 		hres = SafeArrayDestroyDescriptor(a);
-		ok(hres == S_OK,"SADD failed with hres %lx",hres);
+		ok(hres == S_OK,"SADD failed with hres %lx\n",hres);
 	}
 }
+
+static void test_SafeArrayAllocDestroyDescriptor(void)
+{
+  SAFEARRAY *sa;
+  HRESULT hres;
+  int i;
+
+  /* Failure cases */
+  hres = SafeArrayAllocDescriptor(0, &sa);
+  ok(hres == E_INVALIDARG, "0 dimensions gave hres 0x%lx\n", hres);
+
+  hres = SafeArrayAllocDescriptor(65536, &sa);
+  ok(hres == E_INVALIDARG, "65536 dimensions gave hres 0x%lx\n", hres);
+
+#if 0
+  /* Crashes on 95: XP & Wine return E_POINTER */
+  hres=SafeArrayAllocDescriptor(1, NULL);
+  ok(hres == E_POINTER,"NULL parm gave hres 0x%lx\n", hres);
+#endif
+
+  /* Test up to the dimension boundary case */
+  for (i = 5; i <= 65535; i += 30)
+  {
+    hres = SafeArrayAllocDescriptor(i, &sa);
+    ok(hres == S_OK, "%d dimensions failed; hres 0x%lx\n", i, hres);
+
+    if (hres == S_OK)
+    {
+      ok(SafeArrayGetDim(sa) == (UINT)i, "Dimension is %d; should be %d\n",
+         SafeArrayGetDim(sa), i);
+
+      hres = SafeArrayDestroyDescriptor(sa);
+      ok(hres == S_OK, "destroy failed; hres 0x%lx\n", hres);
+    }
+  }
+
+  if (!pSafeArrayAllocDescriptorEx)
+    return;
+
+  hres = pSafeArrayAllocDescriptorEx(VT_UI1, 0, &sa);
+  ok(hres == E_INVALIDARG, "0 dimensions gave hres 0x%lx\n", hres);
+
+  hres = pSafeArrayAllocDescriptorEx(VT_UI1, 65536, &sa);
+  ok(hres == E_INVALIDARG, "65536 dimensions gave hres 0x%lx\n", hres);
+
+  hres = pSafeArrayAllocDescriptorEx(VT_UI1, 1, NULL);
+  ok(hres == E_POINTER,"NULL parm gave hres 0x%lx\n", hres);
+}
+
+static void test_SafeArrayCreateLockDestroy(void)
+{
+  SAFEARRAYBOUND sab[4];
+  SAFEARRAY *sa;
+  HRESULT hres;
+  VARTYPE vt;
+  int dimension;
+
+#define NUM_DIMENSIONS (int)(sizeof(sab) / sizeof(sab[0]))
+
+  for (dimension = 0; dimension < NUM_DIMENSIONS; dimension++)
+  {
+    sab[dimension].lLbound = 0;
+    sab[dimension].cElements = 8;
+  }
+
+  /* Failure cases */
+  sa = SafeArrayCreate(VT_UI1, 1, NULL);
+  ok(sa == NULL, "NULL bounds didn't fail\n");
+
+  sa = SafeArrayCreate(VT_UI1, 65536, sab);
+  ok(sa == NULL, "Max bounds didn't fail\n");
+
+  memset(sab, 0, sizeof(sab));
+
+  /* Don't test 0 sized dimensions, as Windows has a bug which allows this */
+
+  for (dimension = 0; dimension < NUM_DIMENSIONS; dimension++)
+    sab[dimension].cElements = 8;
+
+  /* Test all VARTYPES in 1-4 dimensions */
+  for (dimension = 1; dimension < 4; dimension++)
+  {
+    for (vt = VT_EMPTY; vt < VT_CLSID; vt++)
+    {
+      DWORD dwLen = SAFEARRAY_GetVTSize(vt);
+
+      sa = SafeArrayCreate(vt, dimension, sab);
+
+      if (dwLen)
+        ok(sa != NULL, "VARTYPE %d (@%d dimensions) failed\n", vt, dimension);
+      else
+        ok(sa == NULL, "VARTYPE %d (@%d dimensions) succeeded!\n", vt, dimension);
+
+      if (sa)
+      {
+        ok(SafeArrayGetDim(sa) == (UINT)dimension,
+           "VARTYPE %d (@%d dimensions) cDims is %d, expected %d\n",
+           vt, dimension, SafeArrayGetDim(sa), dimension);
+        ok(SafeArrayGetElemsize(sa) == dwLen,
+           "VARTYPE %d (@%d dimensions) cbElements is %d, expected %ld\n",
+           vt, dimension, SafeArrayGetElemsize(sa), dwLen);
+
+        if (vt != VT_UNKNOWN && vt != VT_DISPATCH)
+        {
+          ok((sa->fFeatures & FADF_HAVEIID) == 0,
+             "Non interface type should not have FADF_HAVEIID\n");
+          if (pSafeArraySetIID)
+          {
+            hres = pSafeArraySetIID(sa, &IID_IUnknown);
+            ok(hres == E_INVALIDARG,
+               "Non interface type allowed SetIID(), hres %lx\n", hres);
+          }
+          if (vt != VT_RECORD)
+          {
+            VARTYPE aVt;
+
+            ok((sa->fFeatures & FADF_HAVEVARTYPE) != 0,
+               "Non interface type should have FADF_HAVEVARTYPE\n");
+            if (pSafeArrayGetVartype)
+            {
+              hres = pSafeArrayGetVartype(sa, &aVt);
+              ok(hres == S_OK && aVt == vt,
+                 "Non interface type %d: bad type %d, hres %lx\n", vt, aVt, hres);
+            }
+          }
+        }
+        else
+        {
+          ok((sa->fFeatures & FADF_HAVEIID) != 0,
+             "Interface type should have FADF_HAVEIID\n");
+          if (pSafeArraySetIID)
+          {
+            hres = pSafeArraySetIID(sa, &IID_IUnknown);
+            ok(hres == S_OK,
+               "Non interface type disallowed SetIID(), hres %lx\n", hres);
+          }
+          ok((sa->fFeatures & FADF_HAVEVARTYPE) == 0,
+             "Interface type %d should not have FADF_HAVEVARTYPE\n", vt);
+        }
+
+        hres = SafeArrayLock(sa);
+        ok(hres == S_OK, "Lock VARTYPE %d (@%d dimensions) failed; hres 0x%lx\n",
+           vt, dimension, hres);
+
+        if (hres == S_OK)
+        {
+          hres = SafeArrayDestroy(sa);
+          ok(hres == DISP_E_ARRAYISLOCKED,"Destroy() got hres %lx\n", hres);
+
+          hres = SafeArrayDestroyData(sa);
+          ok(hres == DISP_E_ARRAYISLOCKED,"DestroyData() got hres %lx\n", hres);
+
+          hres = SafeArrayDestroyDescriptor(sa);
+          ok(hres == DISP_E_ARRAYISLOCKED,"DestroyDescriptor() got hres %lx\n", hres);
+
+          hres = SafeArrayUnlock(sa);
+          ok(hres == S_OK, "Unlock VARTYPE %d (@%d dims) hres 0x%lx\n",
+             vt, dimension, hres);
+
+          hres = SafeArrayDestroyDescriptor(sa);
+          ok(hres == S_OK, "destroy VARTYPE %d (@%d dims) hres 0x%lx\n",
+             vt, dimension, hres);
+        }
+      }
+    }
+  }
+}
+
+static void test_VectorCreateLockDestroy(void)
+{
+  SAFEARRAY *sa;
+  HRESULT hres;
+  VARTYPE vt;
+  int element;
+
+#if 0
+  /* Native allows you to to create 0 sized vectors. Its an ERR in Wine, lets
+   * see if anyone reports it before supporting this brain damage. Actually
+   * using the returned array just crashes in native anyway.
+   */
+  sa = SafeArrayCreateVector(VT_UI1, 0, 0);
+  ok(sa == NULL, "0 elements didn't fail\n");
+#endif
+
+  /* Test all VARTYPES in different lengths */
+  for (element = 1; element <= 101; element += 10)
+  {
+    for (vt = VT_EMPTY; vt < VT_CLSID; vt++)
+    {
+      DWORD dwLen = SAFEARRAY_GetVTSize(vt);
+
+      sa = SafeArrayCreateVector(vt, 0, element);
+
+      if (dwLen)
+        ok(sa != NULL, "VARTYPE %d (@%d elements) failed\n", vt, element);
+      else
+        ok(sa == NULL, "VARTYPE %d (@%d elements) succeeded!\n", vt, element);
+
+      if (sa)
+      {
+        ok(SafeArrayGetDim(sa) == 1, "VARTYPE %d (@%d elements) cDims %d, not 1\n",
+           vt, element, SafeArrayGetDim(sa));
+        ok(SafeArrayGetElemsize(sa) == dwLen,
+           "VARTYPE %d (@%d elements) cbElements is %d, expected %ld\n",
+           vt, element, SafeArrayGetElemsize(sa), dwLen);
+
+        hres = SafeArrayLock(sa);
+        ok(hres == S_OK, "Lock VARTYPE %d (@%d elements) failed; hres 0x%lx\n",
+           vt, element, hres);
+
+        if (hres == S_OK)
+        {
+          hres = SafeArrayUnlock(sa);
+          ok(hres == S_OK, "Unlock VARTYPE %d (@%d elements) failed; hres 0x%lx\n",
+             vt, element, hres);
+
+          hres = SafeArrayDestroyDescriptor(sa);
+          ok(hres == S_OK, "destroy VARTYPE %d (@%d elements) failed; hres 0x%lx\n",
+             vt, element, hres);
+        }
+      }
+    }
+  }
+}
+
+static void test_LockUnlock(void)
+{
+  SAFEARRAYBOUND sab[4];
+  SAFEARRAY *sa;
+  HRESULT hres;
+  BOOL bVector = FALSE;
+  int dimension;
+
+  /* Failure cases */
+  hres = SafeArrayLock(NULL);
+  ok(hres == E_INVALIDARG, "Lock NULL array hres 0x%lx\n", hres);
+  hres = SafeArrayUnlock(NULL);
+  ok(hres == E_INVALIDARG, "Lock NULL array hres 0x%lx\n", hres);
+
+  for (dimension = 0; dimension < NUM_DIMENSIONS; dimension++)
+  {
+    sab[dimension].lLbound = 0;
+    sab[dimension].cElements = 8;
+  }
+
+  sa = SafeArrayCreate(VT_UI1, NUM_DIMENSIONS, sab);
+
+  /* Test maximum locks */
+test_LockUnlock_Vector:
+  if (sa)
+  {
+    int count = 0;
+
+    hres = SafeArrayUnlock(sa);
+    ok (hres == E_UNEXPECTED, "Bad %sUnlock gave hres 0x%lx\n",
+        bVector ? "vector " : "\n", hres);
+
+    while ((hres = SafeArrayLock(sa)) == S_OK)
+      count++;
+    ok (count == 65535 && hres == E_UNEXPECTED, "Lock %sfailed at %d; hres 0x%lx\n",
+        bVector ? "vector " : "\n", count, hres);
+
+    if (count == 65535 && hres == E_UNEXPECTED)
+    {
+      while ((hres = SafeArrayUnlock(sa)) == S_OK)
+        count--;
+      ok (count == 0 && hres == E_UNEXPECTED, "Unlock %sfailed at %d; hres 0x%lx\n",
+          bVector ? "vector " : "\n", count, hres);
+    }
+
+    SafeArrayDestroy(sa);
+  }
+
+  if (bVector == FALSE)
+  {
+    /* Test again with a vector */
+    sa = SafeArrayCreateVector(VT_UI1, 0, 100);
+    bVector = TRUE;
+    goto test_LockUnlock_Vector;
+  }
+}
+
+static void test_SafeArrayGetPutElement(void)
+{
+  SAFEARRAYBOUND sab[4];
+  LONG indices[NUM_DIMENSIONS];
+  SAFEARRAY *sa;
+  HRESULT hres;
+  int value = 0, gotvalue, dimension;
+  unsigned int x,y,z,a;
+
+  for (dimension = 0; dimension < NUM_DIMENSIONS; dimension++)
+  {
+    sab[dimension].lLbound = dimension * 2 + 1;
+    sab[dimension].cElements = dimension * 3 + 1;
+  }
+
+  sa = SafeArrayCreate(VT_INT, NUM_DIMENSIONS, sab);
+  ok(sa != NULL, "4d test couldn't create array\n");
+  if (!sa)
+    return;
+
+  ok(sa->cbElements == sizeof(value), "int size mismatch\n");
+  if (sa->cbElements != sizeof(value))
+    return;
+
+  /* Failure cases */
+  for (x = 0; x < NUM_DIMENSIONS; x++)
+  {
+    indices[0] = sab[0].lLbound;
+    indices[1] = sab[1].lLbound;
+    indices[2] = sab[2].lLbound;
+    indices[3] = sab[3].lLbound;
+
+    indices[x] = indices[x] - 1;
+    hres = SafeArrayPutElement(sa, indices, &value);
+    ok(hres == DISP_E_BADINDEX, "Put allowed too small index in dimension %d\n", x);
+    hres = SafeArrayGetElement(sa, indices, &value);
+    ok(hres == DISP_E_BADINDEX, "Get allowed too small index in dimension %d\n", x);
+
+    indices[x] = sab[x].lLbound + sab[x].cElements;
+    hres = SafeArrayPutElement(sa, indices, &value);
+    ok(hres == DISP_E_BADINDEX, "Put allowed too big index in dimension %d\n", x);
+    hres = SafeArrayGetElement(sa, indices, &value);
+    ok(hres == DISP_E_BADINDEX, "Get allowed too big index in dimension %d\n", x);
+  }
+
+  indices[0] = sab[0].lLbound;
+  indices[1] = sab[1].lLbound;
+  indices[2] = sab[2].lLbound;
+  indices[3] = sab[3].lLbound;
+
+  hres = SafeArrayPutElement(NULL, indices, &value);
+  ok(hres == E_INVALIDARG, "Put NULL array hres 0x%lx\n", hres);
+  hres = SafeArrayGetElement(NULL, indices, &value);
+  ok(hres == E_INVALIDARG, "Get NULL array hres 0x%lx\n", hres);
+
+  hres = SafeArrayPutElement(sa, NULL, &value);
+  ok(hres == E_INVALIDARG, "Put NULL indices hres 0x%lx\n", hres);
+  hres = SafeArrayGetElement(sa, NULL, &value);
+  ok(hres == E_INVALIDARG, "Get NULL indices hres 0x%lx\n", hres);
+
+#if 0
+  /* This is retarded. Windows checks every case of invalid parameters
+   * except the following, which crashes. We ERR this in Wine.
+   */
+  hres = SafeArrayPutElement(sa, indices, NULL);
+  ok(hres == E_INVALIDARG, "Put NULL value hres 0x%lx\n", hres);
+#endif
+
+  hres = SafeArrayGetElement(sa, indices, NULL);
+  ok(hres == E_INVALIDARG, "Get NULL value hres 0x%lx\n", hres);
+
+  value = 0;
+
+  /* Make sure we can read and get back the correct values in 4 dimensions,
+   * Each with a different size and lower bound.
+   */
+  for (x = 0; x < sab[0].cElements; x++)
+  {
+    indices[0] = sab[0].lLbound + x;
+    for (y = 0; y < sab[1].cElements; y++)
+    {
+      indices[1] = sab[1].lLbound + y;
+      for (z = 0; z < sab[2].cElements; z++)
+      {
+        indices[2] = sab[2].lLbound + z;
+        for (a = 0; a < sab[3].cElements; a++)
+        {
+          indices[3] = sab[3].lLbound + a;
+          hres = SafeArrayPutElement(sa, indices, &value);
+          ok(hres == S_OK, "Failed to put element at (%d,%d,%d,%d) hres 0x%lx\n",
+             x, y, z, a, hres);
+          value++;
+        }
+      }
+    }
+  }
+
+  value = 0;
+
+  for (x = 0; x < sab[0].cElements; x++)
+  {
+    indices[0] = sab[0].lLbound + x;
+    for (y = 0; y < sab[1].cElements; y++)
+    {
+      indices[1] = sab[1].lLbound + y;
+      for (z = 0; z < sab[2].cElements; z++)
+      {
+        indices[2] = sab[2].lLbound + z;
+        for (a = 0; a < sab[3].cElements; a++)
+        {
+          indices[3] = sab[3].lLbound + a;
+          gotvalue = value / 3;
+          hres = SafeArrayGetElement(sa, indices, &gotvalue);
+          ok(hres == S_OK, "Failed to get element at (%d,%d,%d,%d) hres 0x%lx\n",
+             x, y, z, a, hres);
+          if (hres == S_OK)
+            ok(value == gotvalue, "Got value %d instead of %d at (%d,%d,%d,%d)\n",
+               gotvalue, value, x, y, z, a);
+          value++;
+        }
+      }
+    }
+  }
+  SafeArrayDestroy(sa);
+}
+
+static void test_SafeArrayCopyData(void)
+{
+  SAFEARRAYBOUND sab[4];
+  SAFEARRAY *sa;
+  SAFEARRAY *sacopy;
+  HRESULT hres;
+  int dimension,size=1;
+
+  if (!pSafeArrayCopyData)
+    return;
+
+  for (dimension = 0; dimension < NUM_DIMENSIONS; dimension++)
+  {
+    sab[dimension].lLbound = dimension * 2 + 2;
+    sab[dimension].cElements = dimension * 3 + 1;
+    size *= sab[dimension].cElements;
+  }
+
+  sa = SafeArrayCreate(VT_INT, NUM_DIMENSIONS, sab);
+  ok(sa != NULL, "Copy test couldn't create array\n");
+  sacopy = SafeArrayCreate(VT_INT, NUM_DIMENSIONS, sab);
+  ok(sacopy != NULL, "Copy test couldn't create copy array\n");
+
+  if (!sa || !sacopy)
+    return;
+
+  ok(sa->cbElements == sizeof(int), "int size mismatch\n");
+  if (sa->cbElements != sizeof(int))
+    return;
+
+  /* Fill the source array with some data; it doesn't matter what */
+  for (dimension = 0; dimension < size; dimension++)
+  {
+    int* data = (int*)sa->pvData;
+    data[dimension] = dimension;
+  }
+
+  hres = pSafeArrayCopyData(sa, sacopy);
+  ok(hres == S_OK, "copy data failed hres 0x%lx\n", hres);
+  if (hres == S_OK)
+  {
+    ok(!memcmp(sa->pvData, sacopy->pvData, size * sizeof(int)), "compared different\n");
+  }
+
+  /* Failure cases */
+  hres = pSafeArrayCopyData(NULL, sacopy);
+  ok(hres == E_INVALIDARG, "Null copy source hres 0x%lx\n", hres);
+  hres = pSafeArrayCopyData(sa, NULL);
+  ok(hres == E_INVALIDARG, "Null copy hres 0x%lx\n", hres);
+
+  sacopy->rgsabound[0].cElements += 1;
+  hres = pSafeArrayCopyData(sa, sacopy);
+  ok(hres == E_INVALIDARG, "Bigger copy first dimension hres 0x%lx\n", hres);
+
+  sacopy->rgsabound[0].cElements -= 2;
+  hres = pSafeArrayCopyData(sa, sacopy);
+  ok(hres == E_INVALIDARG, "Smaller copy first dimension hres 0x%lx\n", hres);
+  sacopy->rgsabound[0].cElements += 1;
+
+  sacopy->rgsabound[3].cElements += 1;
+  hres = pSafeArrayCopyData(sa, sacopy);
+  ok(hres == E_INVALIDARG, "Bigger copy last dimension hres 0x%lx\n", hres);
+
+  sacopy->rgsabound[3].cElements -= 2;
+  hres = pSafeArrayCopyData(sa, sacopy);
+  ok(hres == E_INVALIDARG, "Smaller copy last dimension hres 0x%lx\n", hres);
+  sacopy->rgsabound[3].cElements += 1;
+
+  SafeArrayDestroy(sacopy);
+  sacopy = NULL;
+  hres = pSafeArrayCopyData(sa, sacopy);
+  ok(hres == E_INVALIDARG, "->Null copy hres 0x%lx\n", hres);
+
+  hres = SafeArrayCopy(sa, &sacopy);
+  ok(hres == S_OK, "copy failed hres 0x%lx\n", hres);
+  if (hres == S_OK)
+  {
+    ok(SafeArrayGetElemsize(sa) == SafeArrayGetElemsize(sacopy),"elemsize wrong\n");
+    ok(SafeArrayGetDim(sa) == SafeArrayGetDim(sacopy),"dimensions wrong\n");
+    ok(!memcmp(sa->pvData, sacopy->pvData, size * sizeof(int)), "compared different\n");
+  }
+
+  SafeArrayDestroy(sa);
+}
+
+static void test_SafeArrayCreateEx(void)
+{
+  IRecordInfoImpl* iRec;
+  SAFEARRAYBOUND sab[4];
+  SAFEARRAY *sa;
+  HRESULT hres;
+  int dimension;
+
+  if (!pSafeArrayCreateEx)
+    return;
+
+  for (dimension = 0; dimension < NUM_DIMENSIONS; dimension++)
+  {
+    sab[dimension].lLbound = 0;
+    sab[dimension].cElements = 8;
+  }
+
+  /* Failure cases */
+  sa = pSafeArrayCreateEx(VT_UI1, 1, NULL, NULL);
+  ok(sa == NULL, "CreateEx NULL bounds didn't fail\n");
+
+  /* test IID storage & defaulting */
+  sa = pSafeArrayCreateEx(VT_DISPATCH, 1, sab, (PVOID)&IID_ITypeInfo);
+  ok(sa != NULL, "CreateEx (ITypeInfo) failed\n");
+
+  if (sa)
+  {
+    GUID guid;
+    if (pSafeArrayGetIID)
+    {
+      hres = pSafeArrayGetIID(sa, &guid);
+      ok(hres == S_OK, "CreateEx (ITypeInfo) no IID hres 0x%lx\n", hres);
+      if (hres == S_OK)
+      {
+        ok(IsEqualGUID(&guid, &IID_ITypeInfo), "CreateEx (ITypeInfo) bad IID\n");
+      }
+    }
+    if (pSafeArraySetIID)
+    {
+      hres = pSafeArraySetIID(sa, &IID_IUnknown);
+      ok(hres == S_OK, "Failed to set IID, hres = %8lx\n", hres);
+      if (hres == S_OK && pSafeArrayGetIID)
+      {
+        hres = pSafeArrayGetIID(sa, &guid);
+        ok(hres == S_OK && IsEqualGUID(&guid, &IID_IUnknown), "Set bad IID\n");
+      }
+    }
+    SafeArrayDestroy(sa);
+  }
+
+  sa = pSafeArrayCreateEx(VT_DISPATCH, 1, sab, NULL);
+  ok(sa != NULL, "CreateEx (NULL) failed\n");
+
+  if (sa)
+  {
+    GUID guid;
+    if (pSafeArrayGetIID)
+    {
+      hres = SafeArrayGetIID(sa, &guid);
+      ok(hres == S_OK, "CreateEx (NULL) no IID hres 0x%lx\n", hres);
+      if (hres == S_OK)
+      {
+        ok(IsEqualGUID(&guid, &IID_IDispatch), "CreateEx (NULL) bad IID\n");
+      }
+    }
+    SafeArrayDestroy(sa);
+  }
+
+  sa = pSafeArrayCreateEx(VT_UNKNOWN, 1, sab, NULL);
+  ok(sa != NULL, "CreateEx (NULL-Unk) failed\n");
+
+  if (sa)
+  {
+    GUID guid;
+    if (pSafeArrayGetIID)
+    {
+      hres = SafeArrayGetIID(sa, &guid);
+      ok(hres == S_OK, "CreateEx (NULL-Unk) no IID hres 0x%lx\n", hres);
+      if (hres == S_OK)
+      {
+        ok(IsEqualGUID(&guid, &IID_IUnknown), "CreateEx (NULL-Unk) bad IID\n");
+      }
+    }
+    SafeArrayDestroy(sa);
+  }
+
+  /* VT_RECORD failure case */
+  sa = pSafeArrayCreateEx(VT_RECORD, 1, sab, NULL);
+  ok(sa == NULL, "CreateEx (NULL-Rec) succeded\n");
+
+  iRec = IRecordInfoImpl_Construct();
+
+  /* Win32 doesn't care if GetSize fails */
+  fail_GetSize = TRUE;
+  sa = pSafeArrayCreateEx(VT_RECORD, 1, sab, (LPVOID)iRec);
+  ok(sa != NULL, "CreateEx (Fail Size) failed\n");
+  ok(iRec->ref == START_REF_COUNT + 1, "Wrong iRec refcount %ld\n", iRec->ref);
+  ok(iRec->sizeCalled == 1, "GetSize called %ld times\n", iRec->sizeCalled);
+  ok(iRec->clearCalled == 0, "Clear called %ld times\n", iRec->clearCalled);
+  if (sa)
+  {
+    ok(sa->cbElements == RECORD_SIZE_FAIL, "Altered size to %ld\n", sa->cbElements);
+    SafeArrayDestroy(sa);
+    ok(iRec->clearCalled == sab[0].cElements, "Destroy->Clear called %ld times\n", iRec->clearCalled);
+  }
+
+  /* Test VT_RECORD array */
+  fail_GetSize = FALSE;
+  iRec->ref = START_REF_COUNT;
+  iRec->sizeCalled = 0;
+  iRec->clearCalled = 0;
+  sa = pSafeArrayCreateEx(VT_RECORD, 1, sab, (LPVOID)iRec);
+  ok(sa != NULL, "CreateEx (Rec) failed\n");
+  ok(iRec->ref == START_REF_COUNT + 1, "Wrong iRec refcount %ld\n", iRec->ref);
+  ok(iRec->sizeCalled == 1, "GetSize called %ld times\n", iRec->sizeCalled);
+  ok(iRec->clearCalled == 0, "Clear called %ld times\n", iRec->clearCalled);
+  if (sa)
+  {
+    IRecordInfo* saRec = NULL;
+    hres = SafeArrayGetRecordInfo(sa, &saRec);
+
+    ok(hres == S_OK,"GRI failed\n");
+    ok(saRec == (IRecordInfo*)iRec,"Different saRec\n");
+    ok(iRec->ref == START_REF_COUNT + 2, "Didn't AddRef %ld\n", iRec->ref);
+    if (iRec->ref == START_REF_COUNT + 2)
+      IRecordInfo_Release(saRec);
+
+    ok(sa->cbElements == RECORD_SIZE,"Elemsize is %ld\n", sa->cbElements);
+
+    SafeArrayDestroy(sa);
+    ok(iRec->sizeCalled == 1, "Destroy->GetSize called %ld times\n", iRec->sizeCalled);
+    ok(iRec->clearCalled == sab[0].cElements, "Destroy->Clear called %ld times\n", iRec->clearCalled);
+    ok(iRec->ref == START_REF_COUNT, "Wrong iRec refcount %ld\n", iRec->ref);
+  }
+}
+
+static void test_SafeArrayClear(void)
+{
+  SAFEARRAYBOUND sab;
+  SAFEARRAY *sa;
+  VARIANTARG v;
+  HRESULT hres;
+
+  sab.lLbound = 0;
+  sab.cElements = 10;
+  sa = SafeArrayCreate(VT_UI1, 1, &sab);
+  ok(sa != NULL, "Create() failed.\n");
+  if (!sa)
+    return;
+
+  /* Test clearing non-NULL variants containing arrays */
+  V_VT(&v) = VT_ARRAY|VT_UI1;
+  V_ARRAY(&v) = sa;
+  hres = VariantClear(&v);
+  ok(hres == S_OK && V_VT(&v) == VT_EMPTY, "VariantClear: hres 0x%lx, Type %d\n", hres, V_VT(&v));
+  ok(V_ARRAY(&v) == sa, "VariantClear: Overwrote value\n");
+
+  sa = SafeArrayCreate(VT_UI1, 1, &sab);
+  ok(sa != NULL, "Create() failed.\n");
+  if (!sa)
+    return;
+
+  V_VT(&v) = VT_SAFEARRAY;
+  V_ARRAY(&v) = sa;
+  hres = VariantClear(&v);
+  ok(hres == DISP_E_BADVARTYPE, "VariantClear: hres 0x%lx\n", hres);
+
+  V_VT(&v) = VT_SAFEARRAY|VT_BYREF;
+  V_ARRAYREF(&v) = &sa;
+  hres = VariantClear(&v);
+  ok(hres == DISP_E_BADVARTYPE, "VariantClear: hres 0x%lx\n", hres);
+
+  SafeArrayDestroy(sa);
+}
+
+static void test_SafeArrayCopy(void)
+{
+  SAFEARRAYBOUND sab;
+  SAFEARRAY *sa;
+  VARIANTARG vSrc, vDst;
+  HRESULT hres;
+
+  sab.lLbound = 0;
+  sab.cElements = 10;
+  sa = SafeArrayCreate(VT_UI1, 1, &sab);
+  ok(sa != NULL, "Create() failed.\n");
+  if (!sa)
+    return;
+
+  /* Test copying non-NULL variants containing arrays */
+  V_VT(&vSrc) = (VT_ARRAY|VT_BYREF|VT_UI1);
+  V_ARRAYREF(&vSrc) = &sa;
+  V_VT(&vDst) = VT_EMPTY;
+
+  hres = VariantCopy(&vDst, &vSrc);
+  ok(hres == S_OK && V_VT(&vDst) == (VT_ARRAY|VT_BYREF|VT_UI1),
+     "VariantCopy: hres 0x%lx, Type %d\n", hres, V_VT(&vDst));
+  ok(V_ARRAYREF(&vDst) == &sa, "VariantClear: Performed deep copy\n");
+
+  V_VT(&vSrc) = (VT_ARRAY|VT_UI1);
+  V_ARRAY(&vSrc) = sa;
+  V_VT(&vDst) = VT_EMPTY;
+
+  hres = VariantCopy(&vDst, &vSrc);
+  ok(hres == S_OK && V_VT(&vDst) == (VT_ARRAY|VT_UI1),
+     "VariantCopy: hres 0x%lx, Type %d\n", hres, V_VT(&vDst));
+  ok(V_ARRAY(&vDst) != sa, "VariantClear: Performed shallow copy\n");
+
+  SafeArrayDestroy(V_ARRAY(&vSrc));
+  SafeArrayDestroy(V_ARRAY(&vDst));
+}
+
+#define MKARRAY(low,num,typ) sab.lLbound = low; sab.cElements = num; \
+  sa = SafeArrayCreate(typ, 1, &sab); ok(sa != NULL, "Create() failed.\n"); \
+  if (!sa) return; \
+  V_VT(&v) = VT_ARRAY|typ; V_ARRAY(&v) = sa; VariantInit(&v2)
+
+#define MKARRAYCONT(low,num,typ) sab.lLbound = low; sab.cElements = num; \
+  sa = SafeArrayCreate(typ, 1, &sab); if (!sa) continue; \
+  V_VT(&v) = VT_ARRAY|typ; V_ARRAY(&v) = sa; VariantInit(&v2)
+
+static void test_SafeArrayChangeTypeEx(void)
+{
+  static const char *szHello = "Hello World";
+  SAFEARRAYBOUND sab;
+  SAFEARRAY *sa;
+  VARIANTARG v,v2;
+  VARTYPE vt;
+  HRESULT hres;
+
+  /* VT_ARRAY|VT_UI1 -> VT_BSTR */
+  MKARRAY(0,strlen(szHello)+1,VT_UI1);
+  memcpy(sa->pvData, szHello, strlen(szHello)+1);
+
+  hres = VariantChangeTypeEx(&v2, &v, 0, 0, VT_BSTR);
+  ok(hres == S_OK, "CTE VT_ARRAY|VT_UI1 -> VT_BSTR failed with %lx\n", hres);
+  if (hres == S_OK)
+  {
+    ok(V_VT(&v2) == VT_BSTR, "CTE VT_ARRAY|VT_UI1 -> VT_BSTR did not return VT_BSTR, but %d.\n",V_VT(&v2));
+    ok(strcmp((char*)V_BSTR(&v2),szHello) == 0,"Expected string '%s', got '%s'\n", szHello,
+       (char*)V_BSTR(&v2));
+    VariantClear(&v2);
+  }
+
+  /* VT_VECTOR|VT_UI1 -> VT_BSTR */
+  SafeArrayDestroy(sa);
+  sa = SafeArrayCreateVector(VT_UI1, 0, strlen(szHello)+1);
+  ok(sa != NULL, "CreateVector() failed.\n");
+  if (!sa)
+    return;
+
+  memcpy(sa->pvData, szHello, strlen(szHello)+1);
+  V_VT(&v) = VT_VECTOR|VT_UI1;
+  V_ARRAY(&v) = sa;
+  VariantInit(&v2);
+
+  hres = VariantChangeTypeEx(&v2, &v, 0, 0, VT_BSTR);
+  ok(hres == DISP_E_BADVARTYPE, "CTE VT_VECTOR|VT_UI1 returned %lx\n", hres);
+
+  /* (vector)VT_ARRAY|VT_UI1 -> VT_BSTR (In place) */
+  V_VT(&v) = VT_ARRAY|VT_UI1;
+  hres = VariantChangeTypeEx(&v, &v, 0, 0, VT_BSTR);
+  ok(hres == S_OK, "CTE VT_ARRAY|VT_UI1 -> VT_BSTR failed with %lx\n", hres);
+  if (hres == S_OK)
+  {
+    ok(V_VT(&v) == VT_BSTR, "CTE VT_ARRAY|VT_UI1 -> VT_BSTR did not return VT_BSTR, but %d.\n",V_VT(&v));
+    ok(strcmp((char*)V_BSTR(&v),szHello) == 0,"Expected string '%s', got '%s'\n", szHello,
+              (char*)V_BSTR(&v));
+    VariantClear(&v);
+  }
+
+  /* To/from BSTR only works with arrays of VT_UI1 */
+  for (vt = 0; vt <= VT_CLSID; vt++)
+  {
+    if (vt == VT_UI1)
+      continue;
+
+    MKARRAYCONT(0,1,vt);
+    hres = VariantChangeTypeEx(&v2, &v, 0, 0, VT_BSTR);
+    ok(hres != S_OK, "CTE VT_ARRAY|VT %d->BSTR succeeded\n", vt);
+    VariantClear(&v2);
+  }
+
+  /* Can't change an array of one type into array of another type , even
+   * if the other type is the same size
+   */
+  sa = SafeArrayCreateVector(VT_UI1, 0, 1);
+  ok(sa != NULL, "CreateVector() failed.\n");
+  if (!sa)
+    return;
+
+  V_VT(&v) = VT_ARRAY|VT_UI1;
+  V_ARRAY(&v) = sa;
+  hres = VariantChangeTypeEx(&v2, &v, 0, 0, VT_ARRAY|VT_I1);
+  ok(hres == DISP_E_TYPEMISMATCH, "CTE VT_ARRAY|VT_UI1->VT_ARRAY|VT_I1 returned %lx\n", hres);
+
+  /* But can change to the same array type */
+  SafeArrayDestroy(sa);
+  sa = SafeArrayCreateVector(VT_UI1, 0, 1);
+  ok(sa != NULL, "CreateVector() failed.\n");
+  if (!sa)
+    return;
+  V_VT(&v) = VT_ARRAY|VT_UI1;
+  V_ARRAY(&v) = sa;
+  hres = VariantChangeTypeEx(&v2, &v, 0, 0, VT_ARRAY|VT_UI1);
+  ok(hres == S_OK, "CTE VT_ARRAY|VT_UI1->VT_ARRAY|VT_UI1 returned %lx\n", hres);
+
+  /* NULL/EMPTY */
+  MKARRAY(0,1,VT_UI1);
+  hres = VariantChangeTypeEx(&v2, &v, 0, 0, VT_NULL);
+  ok(hres == DISP_E_TYPEMISMATCH, "CTE VT_ARRAY|VT_UI1 returned %lx\n", hres);
+  MKARRAY(0,1,VT_UI1);
+  hres = VariantChangeTypeEx(&v2, &v, 0, 0, VT_EMPTY);
+  ok(hres == DISP_E_TYPEMISMATCH, "CTE VT_ARRAY|VT_UI1 returned %lx\n", hres);
+
+}
+
+START_TEST(safearray)
+{
+    hOleaut32 = LoadLibraryA("oleaut32.dll");
+
+    GETPTR(SafeArrayAllocDescriptorEx);
+    GETPTR(SafeArrayCopyData);
+    GETPTR(SafeArrayGetIID);
+    GETPTR(SafeArraySetIID);
+    GETPTR(SafeArrayGetVartype);
+    GETPTR(SafeArrayCreateEx);
+
+    test_safearray();
+    test_SafeArrayAllocDestroyDescriptor();
+    test_SafeArrayCreateLockDestroy();
+    test_VectorCreateLockDestroy();
+    test_LockUnlock();
+    test_SafeArrayChangeTypeEx();
+    test_SafeArrayCopy();
+    test_SafeArrayClear();
+    test_SafeArrayCreateEx();
+    test_SafeArrayCopyData();
+    test_SafeArrayGetPutElement();
+}
diff -ur --minimal wine/dlls/oleaut32/variant.c wine-develop/dlls/oleaut32/variant.c
--- wine/dlls/oleaut32/variant.c	2003-12-11 16:24:03.000000000 +0000
+++ wine-develop/dlls/oleaut32/variant.c	2003-12-12 19:53:09.000000000 +0000
@@ -40,7 +40,6 @@
 #include "wine/debug.h"
 #include "wine/unicode.h"
 #include "winerror.h"
-#include "typelib.h"
 #include "variant.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(ole);
@@ -78,13 +77,9 @@
  "|VT_VECTOR|VT_ARRAY|VT_BYREF|VT_HARDTYPE",
 };
 
-/******************************************************************************
- *    Coerce  [INTERNAL]
- *
- * This function dispatches execution to the proper conversion API
- * to do the necessary coercion.
- */
-static HRESULT Coerce(VARIANTARG* pd, LCID lcid, USHORT wFlags, VARIANTARG* ps, VARTYPE vt)
+/* Convert a variant from one type to another */
+static inline HRESULT VARIANT_Coerce(VARIANTARG* pd, LCID lcid, USHORT wFlags,
+                                     VARIANTARG* ps, VARTYPE vt)
 {
   HRESULT res = DISP_E_TYPEMISMATCH;
   VARTYPE vtFrom =  V_TYPE(ps);
@@ -519,11 +514,25 @@
 
   case VT_RECORD:
     break;
-
   }
   return res;
 }
 
+/* Coerce to/from an array */
+static inline HRESULT VARIANT_CoerceArray(VARIANTARG* pd, VARIANTARG* ps, VARTYPE vt)
+{
+  if (vt == VT_BSTR && V_VT(ps) == (VT_ARRAY|VT_UI1))
+    return BstrFromVector(V_ARRAY(ps), &V_BSTR(pd));
+
+  if (V_VT(ps) == VT_BSTR && vt == (VT_ARRAY|VT_UI1))
+    return VectorFromBstr(V_BSTR(ps), &V_ARRAY(ps));
+
+  if (V_VT(ps) == vt)
+    return SafeArrayCopy(V_ARRAY(ps), &V_ARRAY(pd));
+
+  return DISP_E_TYPEMISMATCH;
+}
+
 /******************************************************************************
  * Check if a variants type is valid.
  */
@@ -892,50 +901,6 @@
 }
 
 /******************************************************************************
- * Coerces a full safearray. Not optimal code.
- */
-static HRESULT
-coerce_array(
-	VARIANTARG* src, VARIANTARG *dst, LCID lcid, USHORT wFlags, VARTYPE vt
-) {
-	SAFEARRAY	*sarr = V_ARRAY(src);
-	HRESULT		hres;
-	LPVOID		data;
-	VARTYPE		vartype;
-
-	SafeArrayGetVartype(sarr,&vartype);
-	switch (vt) {
-	case VT_BSTR:
-		if (sarr->cDims != 1) {
-			FIXME("Can not coerce array with dim %d into BSTR\n", sarr->cDims);
-			return E_FAIL;
-		}
-		switch (V_VT(src) & VT_TYPEMASK) {
-		case VT_UI1:
-			hres = SafeArrayAccessData(sarr, &data);
-			if (FAILED(hres)) return hres;
-
-			/* Yes, just memcpied apparently. */
-			V_BSTR(dst) = SysAllocStringByteLen(data, sarr->rgsabound[0].cElements);
-			hres = SafeArrayUnaccessData(sarr);
-			if (FAILED(hres)) return hres;
-			break;
-		default:
-			FIXME("Cannot coerce array of %d into BSTR yet. Please report!\n", V_VT(src) & VT_TYPEMASK);
-			return E_FAIL;
-		}
-		break;
-	case VT_SAFEARRAY:
-		V_VT(dst) = VT_SAFEARRAY;
-		return SafeArrayCopy(sarr, &V_ARRAY(dst));
-	default:
-		FIXME("Cannot coerce array of vt 0x%x/0x%x into vt 0x%x yet. Please report/implement!\n", vartype, V_VT(src), vt);
-		return E_FAIL;
-	}
-	return S_OK;
-}
-
-/******************************************************************************
  *    VariantChangeType  [OLEAUT32.12]
  *
  * Change the type of a variant.
@@ -984,9 +949,6 @@
                                    LCID lcid, USHORT wFlags, VARTYPE vt)
 {
   HRESULT res = S_OK;
-  VARIANTARG varg;
-
-  V_VT(&varg) = VT_EMPTY;
 
   TRACE("(%p->(%s%s),%p->(%s%s),0x%08lx,0x%04x,%s%s)\n", pvargDest,
         debugstr_VT(pvargDest), debugstr_VF(pvargDest), pvargSrc,
@@ -994,84 +956,42 @@
         debugstr_vt(vt), debugstr_vf(vt));
 
   if (vt == VT_CLSID)
-  {
     res = DISP_E_BADVARTYPE;
-  }
   else
+  {
     res = VARIANT_ValidateType(V_VT(pvargSrc));
 
-  if ( SUCCEEDED(res) )
-    res = VARIANT_ValidateType(vt);
+    if (SUCCEEDED(res))
+    {
+      res = VARIANT_ValidateType(vt);
 
-  /* if we are doing an in-place conversion make a copy of the source.
-   */
-  if ( SUCCEEDED(res) && pvargDest == pvargSrc )
-  {
-    res = VariantCopy( &varg, pvargSrc );
-    pvargSrc = &varg;
-  }
+      if (SUCCEEDED(res))
+      {
+        VARIANTARG vTmp;
 
-  if ( SUCCEEDED(res) )
-  {
-    /* free up the destination variant.
-     */
-    res = VariantClear( pvargDest );
-  }
+        V_VT(&vTmp) = VT_EMPTY;
+        res = VariantCopyInd(&vTmp, pvargSrc);
 
-  if ( SUCCEEDED(res) )
-  {
-    if ( V_VT(pvargSrc) & VT_BYREF )
-    {
-      /* Convert the source variant to a "byvalue" variant.
-       */
-      VARIANTARG Variant;
+        if (SUCCEEDED(res))
+        {
+          res = VariantClear(pvargDest);
 
-      if ((V_VT(pvargSrc) & 0xf000) != VT_BYREF) {
-        FIXME("VT_TYPEMASK %s is unhandled.\n", debugstr_VF(pvargSrc));
-        return E_FAIL;
-      }
+          if (SUCCEEDED(res))
+          {
+            if (V_ISARRAY(&vTmp) || (vt & VT_ARRAY))
+              res = VARIANT_CoerceArray(pvargDest, &vTmp, vt);
+            else
+              res = VARIANT_Coerce(pvargDest, lcid, wFlags, &vTmp, vt);
 
-      V_VT(&Variant) = VT_EMPTY;
-      res = VariantCopyInd( &Variant, pvargSrc );
-      if ( SUCCEEDED(res) )
-      {
-        res = Coerce( pvargDest, lcid, wFlags, &Variant, vt );
-        /* this should not fail.
-         */
-        VariantClear( &Variant );
-      }
-    }
-    else
-    {
-      if (V_VT(pvargSrc) & VT_ARRAY)
-      {
-        if ((V_VT(pvargSrc) & 0xf000) != VT_ARRAY)
-        {
-          FIXME("VT_TYPEMASK %s is unhandled in VT_ARRAY.\n", debugstr_VF(pvargSrc));
-          return E_FAIL;
-        }
-        V_VT(pvargDest) = VT_ARRAY | vt;
-        res = coerce_array(pvargSrc, pvargDest, lcid, wFlags, vt);
-      }
-      else
-      {
-        if ((V_VT(pvargSrc) & 0xf000))
-        {
-          FIXME("VT_TYPEMASK %s is unhandled in normal case.\n", debugstr_VF(pvargSrc));
-          return E_FAIL;
+            if (SUCCEEDED(res))
+              V_VT(pvargDest) = vt;
+          }
+          VariantClear(&vTmp);
         }
-        /* Use the current "byvalue" source variant.
-         */
-        res = Coerce( pvargDest, lcid, wFlags, pvargSrc, vt );
       }
     }
   }
 
-  VariantClear( &varg );
-
-  if ( SUCCEEDED(res) )
-    V_VT(pvargDest) = vt;
-
   TRACE("returning 0x%08lx, %p->(%s%s)\n", res, pvargDest,
         debugstr_VT(pvargDest), debugstr_VF(pvargDest));
   return res;
@@ -2318,6 +2238,9 @@
  */
 HRESULT WINAPI VarCat(LPVARIANT left, LPVARIANT right, LPVARIANT out)
 {
+    TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left),
+          debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), out);
+
     /* Should we VariantClear out? */
     /* Can we handle array, vector, by ref etc. */
     if ((V_VT(left)&VT_TYPEMASK) == VT_NULL &&
@@ -2373,8 +2296,6 @@
  */
 HRESULT WINAPI VarCmp(LPVARIANT left, LPVARIANT right, LCID lcid, DWORD flags)
 {
-
-
     BOOL	lOk        = TRUE;
     BOOL	rOk        = TRUE;
     LONGLONG	lVal = -1;
@@ -2383,15 +2304,13 @@
     DWORD	xmask;
     HRESULT	rc;
 
+    TRACE("(%p->(%s%s),%p->(%s%s),0x%08lx,0x%08lx)\n", left, debugstr_VT(left),
+          debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), lcid, flags);
+
     VariantInit(&lv);VariantInit(&rv);
     V_VT(right) &= ~0x8000; /* hack since we sometime get this flag.  */
     V_VT(left) &= ~0x8000; /* hack since we sometime get this flag. */
 
-    TRACE("Left Var:\n");
-    dump_Variant(left);
-    TRACE("Right Var:\n");
-    dump_Variant(right);
-
     /* If either are null, then return VARCMP_NULL */
     if ((V_VT(left)&VT_TYPEMASK) == VT_NULL ||
         (V_VT(right)&VT_TYPEMASK) == VT_NULL)
@@ -2512,10 +2431,8 @@
 {
     HRESULT rc = E_FAIL;
 
-    TRACE("Left Var:\n");
-    dump_Variant(left);
-    TRACE("Right Var:\n");
-    dump_Variant(right);
+    TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left),
+          debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), result);
 
     if ((V_VT(left)&VT_TYPEMASK) == VT_BOOL &&
         (V_VT(right)&VT_TYPEMASK) == VT_BOOL) {
@@ -2581,8 +2498,8 @@
         }
     }
 
-    TRACE("rc=%d, Result:\n", (int) rc);
-    dump_Variant(result);
+    TRACE("returning 0x%8lx (%s%s),%ld\n", rc, debugstr_VT(result),
+          debugstr_VF(result), V_VT(result) == VT_I4 ? V_I4(result) : V_I2(result));
     return rc;
 }
 
@@ -2601,10 +2518,8 @@
 {
     HRESULT rc = E_FAIL;
 
-    TRACE("Left Var:\n");
-    dump_Variant(left);
-    TRACE("Right Var:\n");
-    dump_Variant(right);
+    TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left),
+          debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), result);
 
     if ((V_VT(left)&VT_TYPEMASK) == VT_EMPTY)
     	return VariantCopy(result,right);
@@ -2657,7 +2572,7 @@
             V_UNION(result,dblVal)  = res;
             rc = S_OK;
         } else {
-	    FIXME("Unhandled type pair %d / %d in double addition.\n", 
+	    FIXME("Unhandled type pair %d / %d in double addition.\n",
         	(V_VT(left)&VT_TYPEMASK),
         	(V_VT(right)&VT_TYPEMASK)
 	    );
@@ -2669,7 +2584,7 @@
     if ((V_VT(left)&VT_TYPEMASK) == VT_BSTR &&
         (V_VT(right)&VT_TYPEMASK) == VT_BSTR) {
         V_VT(result) = VT_BSTR;
-        rc = VarBstrCat(V_BSTR(left), V_BSTR(right), &V_BSTR(result));
+        return VarBstrCat(V_BSTR(left), V_BSTR(right), &V_BSTR(result));
     } else {
 
         /* Integers */
@@ -2726,8 +2641,8 @@
         }
     }
 
-    TRACE("rc=%d, Result:\n", (int) rc);
-    dump_Variant(result);
+    TRACE("returning 0x%8lx (%s%s),%ld\n", rc, debugstr_VT(result),
+          debugstr_VF(result), V_VT(result) == VT_I4 ? V_I4(result) : V_I2(result));
     return rc;
 }
 
@@ -2742,8 +2657,8 @@
     VARIANT lv,rv;
     BOOL found;
 
-    TRACE("left: ");dump_Variant(left);
-    TRACE("right: ");dump_Variant(right);
+    TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left),
+          debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), result);
 
     VariantInit(&lv);VariantInit(&rv);
     lvt = V_VT(left)&VT_TYPEMASK;
@@ -2783,8 +2698,8 @@
 	rc = S_OK;
 	break;
     }
-    TRACE("rc=%d, Result:\n", (int) rc);
-    dump_Variant(result);
+    TRACE("returning 0x%8lx (%s%s),%g\n", rc, debugstr_VT(result),
+          debugstr_VF(result), V_VT(result) == VT_R8 ? V_R8(result) : (double)V_I4(result));
     return rc;
 }
 
@@ -2799,8 +2714,8 @@
     VARIANT lv,rv;
     BOOL found;
 
-    TRACE("left: ");dump_Variant(left);
-    TRACE("right: ");dump_Variant(right);
+    TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left),
+          debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), result);
 
     VariantInit(&lv);VariantInit(&rv);
     lvt = V_VT(left)&VT_TYPEMASK;
@@ -2840,8 +2755,8 @@
 	rc = S_OK;
 	break;
     }
-    TRACE("rc=%d, Result:\n", (int) rc);
-    dump_Variant(result);
+    TRACE("returning 0x%8lx (%s%s),%g\n", rc, debugstr_VT(result),
+          debugstr_VF(result), V_VT(result) == VT_R8 ? V_R8(result) : (double)V_I4(result));
     return rc;
 }
 
@@ -2856,8 +2771,8 @@
     VARIANT lv,rv;
     BOOL found;
 
-    TRACE("left: ");dump_Variant(left);
-    TRACE("right: ");dump_Variant(right);
+    TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left),
+          debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), result);
 
     VariantInit(&lv);VariantInit(&rv);
     lvt = V_VT(left)&VT_TYPEMASK;
@@ -2897,8 +2812,8 @@
 	rc = S_OK;
 	break;
     }
-    TRACE("rc=%d, Result:\n", (int) rc);
-    dump_Variant(result);
+    TRACE("returning 0x%8lx (%s%s),%g\n", rc, debugstr_VT(result),
+          debugstr_VF(result), V_VT(result) == VT_R8 ? V_R8(result) : (double)V_I4(result));
     return rc;
 }
 
@@ -2910,10 +2825,8 @@
 {
     HRESULT rc = E_FAIL;
 
-    TRACE("Left Var:\n");
-    dump_Variant(left);
-    TRACE("Right Var:\n");
-    dump_Variant(right);
+    TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left),
+          debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), result);
 
     if ((V_VT(left)&VT_TYPEMASK) == VT_BOOL &&
         (V_VT(right)&VT_TYPEMASK) == VT_BOOL) {
@@ -2979,8 +2892,8 @@
         }
     }
 
-    TRACE("rc=%d, Result:\n", (int) rc);
-    dump_Variant(result);
+    TRACE("returning 0x%8lx (%s%s),%ld\n", rc, debugstr_VT(result),
+          debugstr_VF(result), V_VT(result) == VT_I4 ? V_I4(result) : V_I2(result));
     return rc;
 }
 
@@ -3172,8 +3085,10 @@
  *              VarMod [OLEAUT32.154]
  *
  */
-HRESULT WINAPI VarMod(LPVARIANT left, LPVARIANT right, LPVARIANT result)
+HRESULT WINAPI VarMod(LPVARIANT pVarLeft, LPVARIANT pVarRight, LPVARIANT pVarOut)
 {
-    FIXME("%p %p %p\n", left, right, result);
+    FIXME("(%p->(%s%s),%p->(%s%s),%p)\n", pVarLeft, debugstr_VT(pVarLeft),
+          debugstr_VF(pVarLeft), pVarRight, debugstr_VT(pVarRight),
+          debugstr_VF(pVarRight), pVarOut);
     return E_FAIL;
 }

[Index of Archives]     [Gimp for Windows]     [Red Hat]     [Samba]     [Yosemite Camping]     [Graphics Cards]     [Wine Home]

  Powered by Linux