I8/UI8 are accepted in arrays in recent native dlls.
authorJon Griffiths <jon_p_griffiths@yahoo.com>
Mon, 15 Dec 2003 21:11:25 +0000 (21:11 +0000)
committerAlexandre Julliard <julliard@winehq.org>
Mon, 15 Dec 2003 21:11:25 +0000 (21:11 +0000)
Test UDTs, array coercion and new functions.

dlls/oleaut32/tests/safearray.c

index 4c57e092a6977ecacf819f1f08e70f8ba06c6691..632fe5f954ab8b68023583d5640ece83a55a45ae 100644 (file)
 #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 @@ static struct {
 {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 @@ START_TEST(safearray)
        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 @@ START_TEST(safearray)
 
         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 @@ START_TEST(safearray)
         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();
+}