/* * SafeArray test program * * Copyright 2002 Marcus Meissner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include "wine/test.h" #include "windef.h" #include "winbase.h" #include "winuser.h" #include "wingdi.h" #include "winnls.h" #include "winsock.h" #include "winerror.h" #include "winnt.h" #include "wtypes.h" #include "oleauto.h" 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 HRESULT (WINAPI *pSafeArrayGetRecordInfo)(SAFEARRAY*,IRecordInfo**); static SAFEARRAY* (WINAPI *pSafeArrayCreateEx)(VARTYPE,UINT,SAFEARRAYBOUND*,LPVOID); static SAFEARRAY* (WINAPI *pSafeArrayCreateVector)(VARTYPE,LONG,ULONG); #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) /* Have the decimal type? */ #define HAVE_OLEAUT32_DECIMAL HAVE_FUNC(VarDecAdd) /* very old version? */ #define IS_ANCIENT (!HAVE_FUNC(VarI1FromI2)) #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: if (HAVE_OLEAUT32_I8) return sizeof(LONG64); break; 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: if (HAVE_OLEAUT32_DECIMAL) return sizeof(DECIMAL); break; } return 0; } #define VARTYPE_NOT_SUPPORTED 0 static struct { VARTYPE vt; /* VT */ UINT elemsize; /* elementsize by VT */ UINT expflags; /* fFeatures from SafeArrayAllocDescriptorEx */ UINT addflags; /* additional fFeatures from SafeArrayCreate */ } vttypes[] = { {VT_EMPTY, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_NULL, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_I2, 2, FADF_HAVEVARTYPE,0}, {VT_I4, 4, FADF_HAVEVARTYPE,0}, {VT_R4, 4, FADF_HAVEVARTYPE,0}, {VT_R8, 8, FADF_HAVEVARTYPE,0}, {VT_CY, 8, FADF_HAVEVARTYPE,0}, {VT_DATE, 8, FADF_HAVEVARTYPE,0}, {VT_BSTR, sizeof(BSTR), FADF_HAVEVARTYPE,FADF_BSTR}, {VT_DISPATCH, sizeof(LPDISPATCH), FADF_HAVEIID, FADF_DISPATCH}, {VT_ERROR, 4, FADF_HAVEVARTYPE,0}, {VT_BOOL, 2, FADF_HAVEVARTYPE,0}, {VT_VARIANT, sizeof(VARIANT), FADF_HAVEVARTYPE,FADF_VARIANT}, {VT_UNKNOWN, sizeof(LPUNKNOWN), FADF_HAVEIID, FADF_UNKNOWN}, {VT_DECIMAL, sizeof(DECIMAL), FADF_HAVEVARTYPE,0}, {15, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, /* no VT_xxx */ {VT_I1, 1, FADF_HAVEVARTYPE,0}, {VT_UI1, 1, FADF_HAVEVARTYPE,0}, {VT_UI2, 2, FADF_HAVEVARTYPE,0}, {VT_UI4, 4, FADF_HAVEVARTYPE,0}, {VT_I8, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_UI8, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_INT, sizeof(INT), FADF_HAVEVARTYPE,0}, {VT_UINT, sizeof(UINT), FADF_HAVEVARTYPE,0}, {VT_VOID, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_HRESULT, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_PTR, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_SAFEARRAY,VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_CARRAY, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_USERDEFINED,VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_LPSTR, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_LPWSTR, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_FILETIME, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_RECORD, VARTYPE_NOT_SUPPORTED,FADF_RECORD,0}, {VT_BLOB, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_STREAM, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_STORAGE, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_STREAMED_OBJECT,VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_STORED_OBJECT,VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_BLOB_OBJECT,VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_CF, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, {VT_CLSID, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, }; static void test_safearray(void) { SAFEARRAY *a, b, *c; unsigned int i; long indices[2]; HRESULT hres; SAFEARRAYBOUND bound, bounds[2]; VARIANT v; LPVOID data; IID iid; VARTYPE vt; LONG l; unsigned char *ptr1, *ptr2; bound.cElements = 1; bound.lLbound = 0; a = SafeArrayCreate(-1, 1, &bound); ok(NULL == a,"SAC(-1,1,[1,0]) not failed?\n"); bound.cElements = 0; bound.lLbound = 42; a = SafeArrayCreate(VT_I4, 1, &bound); ok(NULL != a,"SAC(VT_I4,1,[0,0]) failed.\n"); hres = SafeArrayGetLBound(a, 1, &l); ok(hres == S_OK, "SAGLB of 0 size dimensioned array failed with %lx\n",hres); ok(l == 42, "SAGLB of 0 size dimensioned array failed to return 42, but returned %ld\n",l); hres = SafeArrayGetUBound(a, 1, &l); ok(hres == S_OK, "SAGUB of 0 size dimensioned array failed with %lx\n",hres); ok(l == 41, "SAGUB of 0 size dimensioned array failed to return 41, but returned %ld\n",l); bound.cElements = 2; hres = SafeArrayRedim(a, &bound); ok(hres == S_OK,"SAR of a 0 elements dimension failed with hres %lx\n", hres); bound.cElements = 0; hres = SafeArrayRedim(a, &bound); ok(hres == S_OK || hres == E_OUTOFMEMORY, "SAR to a 0 elements dimension failed with hres %lx\n", hres); hres = SafeArrayDestroy(a); ok(hres == S_OK,"SAD of 0 dim array faild with hres %lx\n", hres); bounds[0].cElements = 0; 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,...) with 0 element dim failed.\n"); bounds[0].cElements = 1; bounds[0].lLbound = 1; bounds[1].cElements = 0; bounds[1].lLbound = 23; a = SafeArrayCreate(VT_I4,2,bounds); ok(a != NULL,"SAC(VT_INT32,2,...) with 0 element dim 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.\n"); hres = SafeArrayGetLBound (a, 0, &l); 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\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\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\n", hres); hres = SafeArrayGetUBound (a, 0, &l); 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\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\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\n", hres); i = SafeArrayGetDim(a); 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\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\n",hres); indices[0] = 1; indices[1] = 23; hres = SafeArrayGetElement(a, indices, &i); 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\n",hres); indices[0] = 3; indices[1] = 23; hres = SafeArrayGetElement(a, indices, &i); 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\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\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\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\n",hres); ok(ptr2 - ptr1 == 76,"ptr difference is not 76, but %d (%p vs %p)\n", ptr2-ptr1, ptr2, ptr1); hres = SafeArrayUnaccessData(a); ok(S_OK == hres, "SAUAD failed with 0x%lx\n", hres); for (i=0;icbElements) || (IS_ANCIENT && (vttypes[i].vt == VT_DECIMAL || vttypes[i].vt == VT_I1 || vttypes[i].vt == VT_UI2 || vttypes[i].vt == VT_UI4 || vttypes[i].vt == VT_INT || vttypes[i].vt == VT_UINT)), "SAC(%d,1,[1,0]), %p result %ld, expected %d\n", vttypes[i].vt,a,(a?a->cbElements:0),vttypes[i].elemsize); if (a) { if (!HAVE_OLEAUT32_RECORD) vttypes[i].expflags = 0; ok(a->fFeatures == (vttypes[i].expflags | vttypes[i].addflags), "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\n", vttypes[i].vt, SafeArrayGetElemsize(a),vttypes[i].elemsize); } if (!a) continue; if (pSafeArrayGetVartype) { hres = pSafeArrayGetVartype(a, &vt); 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\n", vt); } else { 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\n", vttypes[i].vt, hres); 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\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\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\n", vt); } else { 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\n", vttypes[i].vt, hres); hres = SafeArrayDestroyData(c); 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\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.\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\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\n"); hres = SafeArrayAccessData(a, &data); 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\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\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\n", hres); hres = SafeArrayUnaccessData(a); ok(hres == S_OK,"SAUD failed after lock/destroy test\n"); hres = SafeArrayDestroy(a); 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\n"); hres = SafeArrayDestroyDescriptor(a); ok(hres == S_OK,"SADD with data in array failed with hres %lx\n",hres); /* IID functions */ /* init a small stack safearray */ if (pSafeArraySetIID) { memset(&b, 0, sizeof(b)); 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\n",hres); hres = SafeArrayAllocDescriptor(1,&a); 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\n",hres); } if (!pSafeArrayAllocDescriptorEx) return; for (i=0;ifFeatures == 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\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\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\n"); ok(IsEqualGUID(&iid, &IID_IDispatch),"SAGIID returned wrong GUID for IDispatch\n"); break; default: 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\n", vttypes[i].vt,hres); } if (a->fFeatures & FADF_RECORD) { 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\n",vttypes[i].vt,((DWORD*)a)[-1]); } hres = pSafeArrayGetVartype(a, &vt); 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\n", vt); } else { 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\n", vttypes[i].vt, hres); hres = pSafeArrayGetIID(a, &iid); 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\n", vttypes[i].vt, hres); } hres = SafeArrayDestroyDescriptor(a); 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(IS_ANCIENT || 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 */ /* This test crashes very early versions with no error checking... sa = SafeArrayCreate(VT_UI1, 1, NULL); ok(sa == NULL, "NULL bounds didn't fail\n"); */ sa = SafeArrayCreate(VT_UI1, 65536, sab); ok(IS_ANCIENT || !sa, "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 || (IS_ANCIENT && (vt == VT_DECIMAL || vt == VT_I1 || vt == VT_UI2 || vt == VT_UI4 || vt == VT_INT || vt == VT_UINT || vt == VT_UINT_PTR || vt == VT_INT_PTR)), "VARTYPE %d (@%d dimensions) failed\n", vt, dimension); else ok(sa == NULL || vt == VT_R8, "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 || vt == VT_R8, "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(IS_ANCIENT || sa->fFeatures & FADF_HAVEVARTYPE, "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(IS_ANCIENT || sa->fFeatures & FADF_HAVEIID, "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 (!pSafeArrayCreateVector) return; sa = pSafeArrayCreateVector(VT_UI1, 0, 0); ok(sa != NULL, "SACV with 0 elements failed.\n"); /* 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 = pSafeArrayCreateVector(vt, 0, element); if (dwLen) { /* win2000 has a bug where U/INT_PTR aren't accepted */ ok(sa != NULL || vt == VT_INT_PTR || vt == VT_UINT_PTR, "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 && pSafeArrayCreateVector) { /* Test again with a vector */ sa = pSafeArrayCreateVector(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); if (!sa) return; /* Some early versions can't handle > 3 dims */ 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_SafeArrayGetPutElement_BSTR(void) { SAFEARRAYBOUND sab; LONG indices[1]; SAFEARRAY *sa; HRESULT hres; BSTR value = 0, gotvalue; const OLECHAR szTest[5] = { 'T','e','s','t','\0' }; sab.lLbound = 1; sab.cElements = 1; sa = SafeArrayCreate(VT_BSTR, 1, &sab); ok(sa != NULL, "BSTR test couldn't create array\n"); if (!sa) return; ok(sa->cbElements == sizeof(BSTR), "BSTR size mismatch\n"); if (sa->cbElements != sizeof(BSTR)) return; indices[0] = sab.lLbound; value = SysAllocString(szTest); ok (value != NULL, "Expected non-NULL\n"); hres = SafeArrayPutElement(sa, indices, value); ok(hres == S_OK, "Failed to put bstr element hres 0x%lx\n", hres); gotvalue = NULL; hres = SafeArrayGetElement(sa, indices, &gotvalue); ok(hres == S_OK, "Failed to get bstr element at hres 0x%lx\n", hres); if (hres == S_OK) ok(SysStringLen(value) == SysStringLen(gotvalue), "Got len %d instead of %d\n", SysStringLen(gotvalue), SysStringLen(value)); SafeArrayDestroy(sa); } static int tunk_xref = 0; static HRESULT WINAPI tunk_QueryInterface(LPUNKNOWN punk,REFIID riid, LPVOID *x) { return E_FAIL; } static ULONG WINAPI tunk_AddRef(LPUNKNOWN punk) { return ++tunk_xref; } static ULONG WINAPI tunk_Release(LPUNKNOWN punk) { return --tunk_xref; } static ICOM_VTABLE(IUnknown) xtunk_vtbl = { ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE tunk_QueryInterface, tunk_AddRef, tunk_Release }; static struct xtunk_iface { ICOM_VTABLE(IUnknown) *lpvtbl; } xtunk_iface; static void test_SafeArrayGetPutElement_IUnknown(void) { SAFEARRAYBOUND sab; LONG indices[1]; SAFEARRAY *sa; HRESULT hres; LPUNKNOWN value = 0, gotvalue; sab.lLbound = 1; sab.cElements = 1; sa = SafeArrayCreate(VT_UNKNOWN, 1, &sab); ok(sa != NULL, "UNKNOWN test couldn't create array\n"); if (!sa) return; ok(sa->cbElements == sizeof(LPUNKNOWN), "LPUNKNOWN size mismatch\n"); if (sa->cbElements != sizeof(LPUNKNOWN)) return; indices[0] = sab.lLbound; xtunk_iface.lpvtbl = &xtunk_vtbl; value = (LPUNKNOWN)&xtunk_iface; tunk_xref = 1; ok (value != NULL, "Expected non-NULL\n"); hres = SafeArrayPutElement(sa, indices, value); ok(hres == S_OK, "Failed to put bstr element hres 0x%lx\n", hres); ok(tunk_xref == 2,"Failed to increment refcount of iface.\n"); gotvalue = NULL; hres = SafeArrayGetElement(sa, indices, &gotvalue); ok(tunk_xref == 3,"Failed to increment refcount of iface.\n"); ok(hres == S_OK, "Failed to get bstr element at hres 0x%lx\n", hres); if (hres == S_OK) ok(value == gotvalue, "Got %p instead of %p\n", gotvalue, value); SafeArrayDestroy(sa); ok(tunk_xref == 2,"Failed to decrement refcount of iface.\n"); } 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 = pSafeArrayGetIID(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 = pSafeArrayGetIID(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 && pSafeArrayGetRecordInfo) { IRecordInfo* saRec = NULL; hres = pSafeArrayGetRecordInfo(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); if (pSafeArrayCreateVector) { sa = pSafeArrayCreateVector(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 */ if (pSafeArrayCreateVector) { sa = pSafeArrayCreateVector(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 = pSafeArrayCreateVector(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); GETPTR(SafeArrayCreateVector); test_safearray(); test_SafeArrayAllocDestroyDescriptor(); test_SafeArrayCreateLockDestroy(); test_VectorCreateLockDestroy(); test_LockUnlock(); test_SafeArrayChangeTypeEx(); test_SafeArrayCopy(); test_SafeArrayClear(); test_SafeArrayCreateEx(); test_SafeArrayCopyData(); test_SafeArrayGetPutElement(); test_SafeArrayGetPutElement_BSTR(); test_SafeArrayGetPutElement_IUnknown(); }