Use correct type of pointer in SafeArrayCreateVector to avoid memory
[wine/hacks.git] / dlls / oleaut32 / safearray.c
blob6ce5b2c8791f77a714a63604f9b4f6703741c34f
1 /*************************************************************************
2 * OLE Automation
3 * SafeArray Implementation
5 * This file contains the implementation of the SafeArray interface.
7 * Copyright 1999 Sylvain St-Germain
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 /* Memory Layout of a SafeArray:
25 * -0x10: start of memory.
26 * -0x10: GUID for VT_DISPATCH and VT_UNKNOWN safearrays (if FADF_HAVEIID)
27 * -0x04: DWORD varianttype; (for all others, except VT_RECORD) (if FADF_HAVEVARTYPE)
28 * -0x4: IRecordInfo* iface; (if FADF_RECORD, for VT_RECORD (can be NULL))
29 * 0x00: SAFEARRAY,
30 * 0x10: SAFEARRAYBOUNDS[0...]
33 #include <stdio.h>
34 #include <string.h>
35 #include "windef.h"
36 #include "winerror.h"
37 #include "winbase.h"
38 #include "oleauto.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(ole);
43 #define SYSDUPSTRING(str) SysAllocStringLen((str), SysStringLen(str))
45 /* Locally used methods */
46 static INT
47 endOfDim(LONG *coor, SAFEARRAYBOUND *mat, LONG dim, LONG realDim);
49 static ULONG
50 calcDisplacement(LONG *coor, SAFEARRAYBOUND *mat, LONG dim);
52 static BOOL
53 isPointer(USHORT feature);
55 static INT
56 getFeatures(VARTYPE vt);
58 static BOOL
59 validCoordinate(LONG *coor, SAFEARRAY *psa);
61 static BOOL
62 resizeSafeArray(SAFEARRAY *psa, LONG lDelta);
64 static BOOL
65 validArg(SAFEARRAY *psa);
67 static ULONG
68 getArraySize(SAFEARRAY *psa);
70 static HRESULT
71 duplicateData(SAFEARRAY *psa, SAFEARRAY *ppsaOut);
73 /* Association between VARTYPE and their size.
74 A size of zero is defined for the unsupported types. */
76 #define VARTYPE_NOT_SUPPORTED 0
77 static const ULONG VARTYPE_SIZE[] =
79 /* this is taken from wtypes.h. Only [S]es are supported by the SafeArray */
80 VARTYPE_NOT_SUPPORTED, /* VT_EMPTY [V] [P] nothing */
81 VARTYPE_NOT_SUPPORTED, /* VT_NULL [V] [P] SQL style Nul */
82 2, /* VT_I2 [V][T][P][S] 2 byte signed int */
83 4, /* VT_I4 [V][T][P][S] 4 byte signed int */
84 4, /* VT_R4 [V][T][P][S] 4 byte real */
85 8, /* VT_R8 [V][T][P][S] 8 byte real */
86 8, /* VT_CY [V][T][P][S] currency */
87 8, /* VT_DATE [V][T][P][S] date */
88 sizeof(BSTR), /* VT_BSTR [V][T][P][S] OLE Automation string*/
89 sizeof(LPDISPATCH), /* VT_DISPATCH [V][T][P][S] IDispatch * */
90 4, /* VT_ERROR [V][T] [S] SCODE */
91 2, /* VT_BOOL [V][T][P][S] True=-1, False=0*/
92 sizeof(VARIANT), /* VT_VARIANT [V][T][P][S] VARIANT * */
93 sizeof(LPUNKNOWN), /* VT_UNKNOWN [V][T] [S] IUnknown * */
94 sizeof(DECIMAL), /* VT_DECIMAL [V][T] [S] 16 byte fixed point */
95 VARTYPE_NOT_SUPPORTED, /* no VARTYPE here..... */
96 1, /* VT_I1 [T] [S] signed char */
97 1, /* VT_UI1 [V][T][P][S] unsigned char */
98 2, /* VT_UI2 [T][P][S] unsigned short */
99 4, /* VT_UI4 [T][P][S] unsigned int */
100 VARTYPE_NOT_SUPPORTED, /* VT_I8 [T][P] signed 64-bit int */
101 VARTYPE_NOT_SUPPORTED, /* VT_UI8 [T][P] unsigned 64-bit int */
102 sizeof(INT), /* VT_INT [T] signed machine int */
103 sizeof(UINT), /* VT_UINT [T] unsigned machine int */
104 VARTYPE_NOT_SUPPORTED, /* VT_VOID [T] C style void */
105 VARTYPE_NOT_SUPPORTED, /* VT_HRESULT [T] Standard return type */
106 VARTYPE_NOT_SUPPORTED, /* VT_PTR [T] pointer type */
107 VARTYPE_NOT_SUPPORTED, /* VT_SAFEARRAY [T] (use VT_ARRAY in VARIANT)*/
108 VARTYPE_NOT_SUPPORTED, /* VT_CARRAY [T] C style array */
109 VARTYPE_NOT_SUPPORTED, /* VT_USERDEFINED [T] user defined type */
110 VARTYPE_NOT_SUPPORTED, /* VT_LPSTR [T][P] null terminated string */
111 VARTYPE_NOT_SUPPORTED, /* VT_LPWSTR [T][P] wide null term string */
112 VARTYPE_NOT_SUPPORTED, /* 32 */
113 VARTYPE_NOT_SUPPORTED, /* 33 */
114 VARTYPE_NOT_SUPPORTED, /* 34 */
115 VARTYPE_NOT_SUPPORTED, /* 35 */
116 VARTYPE_NOT_SUPPORTED, /* VT_RECORD record */
117 VARTYPE_NOT_SUPPORTED, /* 37 */
118 VARTYPE_NOT_SUPPORTED, /* 38 */
119 VARTYPE_NOT_SUPPORTED, /* 39 */
120 VARTYPE_NOT_SUPPORTED, /* 40 */
121 VARTYPE_NOT_SUPPORTED, /* 41 */
122 VARTYPE_NOT_SUPPORTED, /* 42 */
123 VARTYPE_NOT_SUPPORTED, /* 43 */
124 VARTYPE_NOT_SUPPORTED, /* 44 */
125 VARTYPE_NOT_SUPPORTED, /* 45 */
126 VARTYPE_NOT_SUPPORTED, /* 46 */
127 VARTYPE_NOT_SUPPORTED, /* 47 */
128 VARTYPE_NOT_SUPPORTED, /* 48 */
129 VARTYPE_NOT_SUPPORTED, /* 49 */
130 VARTYPE_NOT_SUPPORTED, /* 50 */
131 VARTYPE_NOT_SUPPORTED, /* 51 */
132 VARTYPE_NOT_SUPPORTED, /* 52 */
133 VARTYPE_NOT_SUPPORTED, /* 53 */
134 VARTYPE_NOT_SUPPORTED, /* 54 */
135 VARTYPE_NOT_SUPPORTED, /* 55 */
136 VARTYPE_NOT_SUPPORTED, /* 56 */
137 VARTYPE_NOT_SUPPORTED, /* 57 */
138 VARTYPE_NOT_SUPPORTED, /* 58 */
139 VARTYPE_NOT_SUPPORTED, /* 59 */
140 VARTYPE_NOT_SUPPORTED, /* 60 */
141 VARTYPE_NOT_SUPPORTED, /* 61 */
142 VARTYPE_NOT_SUPPORTED, /* 62 */
143 VARTYPE_NOT_SUPPORTED, /* 63 */
144 VARTYPE_NOT_SUPPORTED, /* VT_FILETIME [P] FILETIME */
145 VARTYPE_NOT_SUPPORTED, /* VT_BLOB [P] Length prefixed bytes */
146 VARTYPE_NOT_SUPPORTED, /* VT_STREAM [P] Name of stream follows */
147 VARTYPE_NOT_SUPPORTED, /* VT_STORAGE [P] Name of storage follows */
148 VARTYPE_NOT_SUPPORTED, /* VT_STREAMED_OBJECT[P] Stream contains an object*/
149 VARTYPE_NOT_SUPPORTED, /* VT_STORED_OBJECT [P] Storage contains object*/
150 VARTYPE_NOT_SUPPORTED, /* VT_BLOB_OBJECT [P] Blob contains an object*/
151 VARTYPE_NOT_SUPPORTED, /* VT_CF [P] Clipboard format */
152 VARTYPE_NOT_SUPPORTED, /* VT_CLSID [P] A Class ID */
155 static const int LAST_VARTYPE = sizeof(VARTYPE_SIZE)/sizeof(VARTYPE_SIZE[0]);
158 /*************************************************************************
159 * SafeArrayAllocDescriptor (OLEAUT32.36)
160 * Allocate the appropriate amount of memory for the SafeArray descriptor
162 HRESULT WINAPI SafeArrayAllocDescriptor(
163 UINT cDims,
164 SAFEARRAY **ppsaOut)
166 SAFEARRAYBOUND *sab;
167 LONG allocSize = 0;
168 char *ptr;
170 if (!cDims || cDims >= 0x10000) /* 65536 appears to be the limit */
171 return E_INVALIDARG;
172 if (!ppsaOut)
173 return E_POINTER;
175 /* GUID + SAFEARRAY + SAFEARRAYBOUND * (cDims -1)
176 * ( -1 because there is already one ( in SAFEARRAY struct
178 allocSize = sizeof(GUID) + sizeof(**ppsaOut) + (sizeof(*sab) * (cDims-1));
180 /* Allocate memory for SAFEARRAY struc */
181 ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, allocSize);
182 if (!ptr)
183 return E_OUTOFMEMORY;
184 *ppsaOut = (SAFEARRAY *)(ptr + sizeof(GUID));
185 (*ppsaOut)->cDims = cDims;
186 TRACE("(%d): %lu bytes allocated for descriptor.\n", cDims, allocSize);
188 return(S_OK);
191 /*************************************************************************
192 * SafeArrayAllocDescriptorEx (OLEAUT32.41)
193 * Allocate the appropriate amount of memory for the SafeArray descriptor
194 * and also store information about the vartype before the returned pointer.
196 HRESULT WINAPI SafeArrayAllocDescriptorEx(
197 VARTYPE vt,
198 UINT cDims,
199 SAFEARRAY **ppsaOut)
201 HRESULT hres;
203 hres = SafeArrayAllocDescriptor (cDims, ppsaOut);
204 if (FAILED(hres))
205 return hres;
207 switch (vt) {
208 case VT_DISPATCH:
209 (*ppsaOut)->fFeatures = FADF_HAVEIID;
210 SafeArraySetIID( *ppsaOut, &IID_IDispatch);
211 break;
212 case VT_UNKNOWN:
213 (*ppsaOut)->fFeatures = FADF_HAVEIID;
214 SafeArraySetIID( *ppsaOut, &IID_IUnknown);
215 break;
216 case VT_RECORD:
217 (*ppsaOut)->fFeatures = FADF_RECORD;
218 break;
219 default:
220 (*ppsaOut)->fFeatures = FADF_HAVEVARTYPE;
221 ((DWORD*)*ppsaOut)[-1] = vt;
222 break;
224 return S_OK;
227 /*************************************************************************
228 * SafeArrayAllocData (OLEAUT32.37)
229 * Allocate the appropriate amount of data for the SafeArray data
231 HRESULT WINAPI SafeArrayAllocData(
232 SAFEARRAY *psa)
234 ULONG ulWholeArraySize; /* to store the size of the whole thing */
236 if(! validArg(psa))
237 return E_INVALIDARG;
239 ulWholeArraySize = getArraySize(psa);
241 /* Allocate memory for the data itself */
242 if((psa->pvData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
243 psa->cbElements*ulWholeArraySize)) == NULL)
244 return(E_UNEXPECTED);
246 TRACE("SafeArray: %lu bytes allocated for data at %p (%lu objects).\n",
247 psa->cbElements*ulWholeArraySize, psa->pvData, ulWholeArraySize);
249 return(S_OK);
252 /*************************************************************************
253 * SafeArrayCreate (OLEAUT32.15)
254 * Create a SafeArray object by encapsulating AllocDescriptor and AllocData
256 SAFEARRAY* WINAPI SafeArrayCreate(
257 VARTYPE vt,
258 UINT cDims,
259 SAFEARRAYBOUND *rgsabound)
261 SAFEARRAY *psa;
262 HRESULT hRes;
263 USHORT cDim;
265 TRACE("(%d, %d, %p)\n", vt, cDims, rgsabound);
267 /* Validate supported VARTYPE */
268 if ( (vt >= LAST_VARTYPE) ||
269 ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
270 return NULL;
272 /* Allocate memory for the array descriptor */
273 if( FAILED( hRes = SafeArrayAllocDescriptorEx(vt, cDims, &psa)))
274 return NULL;
276 /* setup data members... */
277 psa->cDims = cDims;
278 switch (vt) {
279 case VT_BSTR: psa->fFeatures |= FADF_BSTR;break;
280 case VT_UNKNOWN: psa->fFeatures |= FADF_UNKNOWN;break;
281 case VT_DISPATCH: psa->fFeatures |= FADF_DISPATCH;break;
282 case VT_VARIANT: psa->fFeatures |= FADF_VARIANT;break;
283 default: break;
285 psa->cLocks = 0;
286 psa->pvData = NULL;
287 psa->cbElements= VARTYPE_SIZE[vt];
289 /* Invert the bounds ... */
290 for(cDim=0; cDim < psa->cDims; cDim++) {
291 psa->rgsabound[cDim].cElements = rgsabound[psa->cDims-cDim-1].cElements;
292 psa->rgsabound[cDim].lLbound = rgsabound[psa->cDims-cDim-1].lLbound;
295 /* allocate memory for the data... */
296 if( FAILED( hRes = SafeArrayAllocData(psa))) {
297 SafeArrayDestroyDescriptor(psa);
298 ERR("() : Failed to allocate the Safe Array data\n");
299 return NULL;
302 return(psa);
305 /*************************************************************************
306 * SafeArrayDestroyDescriptor (OLEAUT32.38)
307 * Frees the memory associated with the descriptor.
309 HRESULT WINAPI SafeArrayDestroyDescriptor(
310 SAFEARRAY *psa)
312 LPVOID ptr;
314 /* Check for lockness before to free... */
315 if(psa->cLocks > 0)
316 return DISP_E_ARRAYISLOCKED;
318 /* The array is unlocked, then, deallocate memory */
319 ptr = ((IID*)psa)-1;
320 if(HeapFree( GetProcessHeap(), 0, ptr) == FALSE)
321 return E_UNEXPECTED;
322 return(S_OK);
326 /*************************************************************************
327 * SafeArrayLock (OLEAUT32.21)
328 * Increment the lock counter
330 * Doc says (MSDN Library ) that psa->pvData should be made available (!= NULL)
331 * only when psa->cLocks is > 0... I don't get it since pvData is allocated
332 * before the array is locked, therefore
334 HRESULT WINAPI SafeArrayLock(
335 SAFEARRAY *psa)
337 if(! validArg(psa))
338 return E_INVALIDARG;
340 psa->cLocks++;
342 return(S_OK);
345 /*************************************************************************
346 * SafeArrayUnlock (OLEAUT32.22)
347 * Decrement the lock counter
349 HRESULT WINAPI SafeArrayUnlock(
350 SAFEARRAY *psa)
352 if(! validArg(psa))
353 return E_INVALIDARG;
355 if (psa->cLocks > 0)
356 psa->cLocks--;
358 return(S_OK);
362 /*************************************************************************
363 * SafeArrayPutElement (OLEAUT32.26)
364 * Set the data at the given coordinate
366 HRESULT WINAPI SafeArrayPutElement(
367 SAFEARRAY *psa,
368 LONG *rgIndices,
369 void *pv)
371 ULONG stepCountInSAData = 0; /* Number of array item to skip to get to
372 the desired one... */
373 PVOID elementStorageAddress = NULL; /* Adress to store the data */
375 /* Validate the index given */
376 if(! validCoordinate(rgIndices, psa))
377 return DISP_E_BADINDEX;
378 if(! validArg(psa))
379 return E_INVALIDARG;
381 if( SafeArrayLock(psa) == S_OK) {
383 /* Figure out the number of items to skip */
384 stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
386 /* Figure out the number of byte to skip ... */
387 elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
389 if(isPointer(psa->fFeatures)) { /* increment ref count for this pointer */
391 *((PVOID*)elementStorageAddress) = *(PVOID*)pv;
392 IUnknown_AddRef( *(IUnknown**)pv);
394 } else {
396 if(psa->fFeatures & FADF_BSTR) { /* Create a new object */
397 BSTR pbstrReAllocStr = NULL;
398 if(pv &&
399 ((pbstrReAllocStr = SYSDUPSTRING( (OLECHAR*)pv )) == NULL)) {
400 SafeArrayUnlock(psa);
401 return E_OUTOFMEMORY;
402 } else
403 *((BSTR*)elementStorageAddress) = pbstrReAllocStr;
405 else if(psa->fFeatures & FADF_VARIANT) {
406 HRESULT hr = VariantCopy(elementStorageAddress, pv);
407 if (FAILED(hr)) {
408 SafeArrayUnlock(psa);
409 return hr;
412 else /* duplicate the memory */
413 memcpy(elementStorageAddress, pv, SafeArrayGetElemsize(psa) );
416 } else {
417 ERR("SafeArray: Cannot lock array....\n");
418 return E_UNEXPECTED; /* UNDOC error condition */
421 TRACE("SafeArray: item put at adress %p.\n",elementStorageAddress);
422 return SafeArrayUnlock(psa);
426 /*************************************************************************
427 * SafeArrayGetElement (OLEAUT32.25)
428 * Return the data element corresponding the the given coordinate
430 HRESULT WINAPI SafeArrayGetElement(
431 SAFEARRAY *psa,
432 LONG *rgIndices,
433 void *pv)
435 ULONG stepCountInSAData = 0; /* Number of array item to skip to get to
436 the desired one... */
437 PVOID elementStorageAddress = NULL; /* Adress to store the data */
439 if(! validArg(psa))
440 return E_INVALIDARG;
442 if(! validCoordinate(rgIndices, psa)) /* Validate the index given */
443 return(DISP_E_BADINDEX);
445 if( SafeArrayLock(psa) == S_OK) {
447 /* Figure out the number of items to skip */
448 stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
450 /* Figure out the number of byte to skip ... */
451 elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
453 if( psa->fFeatures & FADF_BSTR) { /* reallocate the obj */
454 BSTR pbstrStoredStr = *(OLECHAR**)elementStorageAddress;
455 BSTR pbstrReturnedStr = NULL;
456 if( pbstrStoredStr &&
457 ((pbstrReturnedStr = SYSDUPSTRING( pbstrStoredStr )) == NULL) ) {
458 SafeArrayUnlock(psa);
459 return E_OUTOFMEMORY;
460 } else
461 *((BSTR*)pv) = pbstrReturnedStr;
463 else if( psa->fFeatures & FADF_VARIANT) {
464 HRESULT hr;
465 VariantInit(pv);
466 hr = VariantCopy(pv, elementStorageAddress);
467 if (FAILED(hr)) {
468 SafeArrayUnlock(psa);
469 return hr;
472 else if( isPointer(psa->fFeatures) ) /* simply copy the pointer */
473 *(PVOID*)pv = *((PVOID*)elementStorageAddress);
474 else /* copy the bytes */
475 memcpy(pv, elementStorageAddress, psa->cbElements );
477 } else {
478 ERR("SafeArray: Cannot lock array....\n");
479 return E_UNEXPECTED; /* UNDOC error condition */
482 return( SafeArrayUnlock(psa) );
485 /*************************************************************************
486 * SafeArrayGetUBound (OLEAUT32.19)
487 * return the UP bound for a given array dimension
489 HRESULT WINAPI SafeArrayGetUBound(
490 SAFEARRAY *psa,
491 UINT nDim,
492 LONG *plUbound)
494 if(! validArg(psa))
495 return E_INVALIDARG;
497 if(nDim > psa->cDims)
498 return DISP_E_BADINDEX;
500 if(0 == nDim)
501 return DISP_E_BADINDEX;
503 *plUbound = psa->rgsabound[nDim-1].lLbound +
504 psa->rgsabound[nDim-1].cElements - 1;
506 return S_OK;
509 /*************************************************************************
510 * SafeArrayGetLBound (OLEAUT32.20)
511 * Return the LO bound for a given array dimension
513 HRESULT WINAPI SafeArrayGetLBound(
514 SAFEARRAY *psa,
515 UINT nDim,
516 LONG *plLbound)
518 if(! validArg(psa))
519 return E_INVALIDARG;
521 if(nDim > psa->cDims)
522 return DISP_E_BADINDEX;
524 if(0 == nDim)
525 return DISP_E_BADINDEX;
527 *plLbound = psa->rgsabound[nDim-1].lLbound;
528 return S_OK;
531 /*************************************************************************
532 * SafeArrayGetDim (OLEAUT32.17)
533 * returns the number of dimension in the array
535 UINT WINAPI SafeArrayGetDim(
536 SAFEARRAY * psa)
539 * A quick test in Windows shows that the behavior here for an invalid
540 * pointer is to return 0.
542 if(! validArg(psa))
543 return 0;
545 return psa->cDims;
548 /*************************************************************************
549 * SafeArrayGetElemsize (OLEAUT32.18)
550 * Return the size of the element in the array
552 UINT WINAPI SafeArrayGetElemsize(
553 SAFEARRAY * psa)
556 * A quick test in Windows shows that the behavior here for an invalid
557 * pointer is to return 0.
559 if(! validArg(psa))
560 return 0;
562 return psa->cbElements;
565 /*************************************************************************
566 * SafeArrayAccessData (OLEAUT32.23)
567 * increment the access count and return the data
569 HRESULT WINAPI SafeArrayAccessData(
570 SAFEARRAY *psa,
571 void **ppvData)
573 HRESULT hRes;
575 if(! validArg(psa))
576 return E_INVALIDARG;
578 hRes = SafeArrayLock(psa);
580 switch (hRes) {
581 case S_OK:
582 (*ppvData) = psa->pvData;
583 break;
584 case E_INVALIDARG:
585 (*ppvData) = NULL;
586 return E_INVALIDARG;
589 return S_OK;
593 /*************************************************************************
594 * SafeArrayUnaccessData (OLEAUT32.24)
595 * Decrement the access count
597 HRESULT WINAPI SafeArrayUnaccessData(
598 SAFEARRAY * psa)
600 if(! validArg(psa))
601 return E_INVALIDARG;
603 return(SafeArrayUnlock(psa));
606 /************************************************************************
607 * SafeArrayPtrOfIndex (OLEAUT32.148)
608 * Return a pointer to the element at rgIndices
610 HRESULT WINAPI SafeArrayPtrOfIndex(
611 SAFEARRAY *psa,
612 LONG *rgIndices,
613 void **ppvData)
615 ULONG stepCountInSAData = 0; /* Number of array item to skip to get to
616 the desired one... */
618 if(! validArg(psa))
619 return E_INVALIDARG;
621 if(! validCoordinate(rgIndices, psa))
622 return DISP_E_BADINDEX;
624 /* Although it is dangerous to do this without having a lock, it is not
625 * illegal. Microsoft do warn of the danger.
628 /* Figure out the number of items to skip */
629 stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
631 *ppvData = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
633 return S_OK;
636 /************************************************************************
637 * SafeArrayDestroyData (OLEAUT32.39)
638 * Frees the memory data bloc
640 HRESULT WINAPI SafeArrayDestroyData(
641 SAFEARRAY *psa)
643 HRESULT hRes;
644 ULONG ulWholeArraySize; /* count spot in array */
645 ULONG ulDataIter; /* to iterate the data space */
647 if(! validArg(psa))
648 return E_INVALIDARG;
650 if(psa->cLocks > 0)
651 return DISP_E_ARRAYISLOCKED;
653 if(psa->pvData==NULL)
654 return S_OK;
656 ulWholeArraySize = getArraySize(psa);
658 if(isPointer(psa->fFeatures)) { /* release the pointers */
659 IUnknown *punk;
661 for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
662 punk = *(IUnknown**)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));
664 if( punk != NULL)
665 IUnknown_Release(punk);
669 else if(psa->fFeatures & FADF_BSTR) { /* deallocate the obj */
670 BSTR bstr;
672 for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
673 bstr = *(BSTR*)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));
675 if( bstr != NULL)
676 SysFreeString( bstr );
679 else if(psa->fFeatures & FADF_VARIANT) { /* deallocate the obj */
681 for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
682 VariantClear((VARIANT*)((char *) psa->pvData+(ulDataIter*(psa->cbElements))));
686 /* check if this array is a Vector, in which case do not free the data
687 block since it has been allocated by AllocDescriptor and therefore
688 deserve to be freed by DestroyDescriptor */
689 if(!(psa->fFeatures & FADF_CREATEVECTOR)) { /* Set when we do CreateVector */
691 /* free the whole chunk */
692 if((hRes = HeapFree( GetProcessHeap(), 0, psa->pvData)) == 0) /*failed*/
693 return E_UNEXPECTED; /* UNDOC error condition */
695 psa->pvData = NULL;
698 return S_OK;
701 /************************************************************************
702 * SafeArrayCopyData (OLEAUT32.412)
703 * Copy the psaSource's data block into psaTarget if dimension and size
704 * permits it.
706 HRESULT WINAPI SafeArrayCopyData(
707 SAFEARRAY *psaSource,
708 SAFEARRAY *psaTarget)
710 USHORT cDimCount; /* looper */
711 LONG lDelta; /* looper */
712 IUnknown *punk;
713 ULONG ulWholeArraySize; /* Number of item in SA */
714 BSTR bstr;
716 if(! (validArg(psaSource) && validArg(psaTarget)) )
717 return E_INVALIDARG;
719 if(SafeArrayGetDim(psaSource) != SafeArrayGetDim(psaTarget))
720 return E_INVALIDARG;
722 ulWholeArraySize = getArraySize(psaSource);
724 /* The two arrays boundaries must be of same lenght */
725 for(cDimCount=0;cDimCount < psaSource->cDims; cDimCount++)
726 if( psaSource->rgsabound[cDimCount].cElements !=
727 psaTarget->rgsabound[cDimCount].cElements)
728 return E_INVALIDARG;
730 if( isPointer(psaTarget->fFeatures) ) { /* the target contains ptr
731 that must be released */
732 for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
733 punk = *(IUnknown**)
734 ((char *) psaTarget->pvData + (lDelta * psaTarget->cbElements));
736 if( punk != NULL)
737 IUnknown_Release(punk);
741 else if( psaTarget->fFeatures & FADF_BSTR) { /* the target contain BSTR
742 that must be freed */
743 for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
744 bstr =
745 *(BSTR*)((char *) psaTarget->pvData + (lDelta * psaTarget->cbElements));
747 if( bstr != NULL)
748 SysFreeString( bstr );
751 else if( psaTarget->fFeatures & FADF_VARIANT) {
753 for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
754 VariantClear((VARIANT*)((char *) psaTarget->pvData + (lDelta * psaTarget->cbElements)));
758 return duplicateData(psaSource, psaTarget);
761 /************************************************************************
762 * SafeArrayDestroy (OLEAUT32.16)
763 * Deallocates all memory reserved for the SafeArray
765 HRESULT WINAPI SafeArrayDestroy(
766 SAFEARRAY * psa)
768 HRESULT hRes;
770 if(! validArg(psa))
771 return E_INVALIDARG;
773 if(psa->cLocks > 0)
774 return DISP_E_ARRAYISLOCKED;
776 if((hRes = SafeArrayDestroyData( psa )) == S_OK)
777 if((hRes = SafeArrayDestroyDescriptor( psa )) == S_OK)
778 return S_OK;
780 return E_UNEXPECTED; /* UNDOC error condition */
783 /************************************************************************
784 * SafeArrayCopy (OLEAUT32.27)
785 * Make a dupplicate of a SafeArray
787 HRESULT WINAPI SafeArrayCopy(
788 SAFEARRAY *psa,
789 SAFEARRAY **ppsaOut)
791 HRESULT hRes;
792 DWORD dAllocSize;
793 ULONG ulWholeArraySize; /* size of the thing */
795 if(! validArg(psa))
796 return E_INVALIDARG;
798 if((hRes=SafeArrayAllocDescriptor(psa->cDims, ppsaOut)) == S_OK){
800 /* Duplicate the SAFEARRAY struct */
801 memcpy(*ppsaOut, psa,
802 sizeof(*psa)+(sizeof(*(psa->rgsabound))*(psa->cDims-1)));
804 /* If the features that use storage before the SAFEARRAY struct are
805 * enabled, also copy this memory range. Flags have been copied already.
807 if (psa->fFeatures & (FADF_HAVEIID | FADF_HAVEVARTYPE))
808 memcpy(((GUID*)*ppsaOut)-1, ((GUID*)psa)-1, sizeof(GUID));
810 /* Copy the IRecordInfo* reference */
811 if (psa->fFeatures & FADF_RECORD) {
812 IRecordInfo *ri;
814 ri = ((IRecordInfo**)psa)[-1];
815 if (ri) {
816 ((IRecordInfo**)*ppsaOut)[-1] = ri;
817 IRecordInfo_AddRef(ri);
821 (*ppsaOut)->pvData = NULL; /* do not point to the same data area */
823 /* make sure the new safe array doesn't have the FADF_CREATEVECTOR flag,
824 because the data has not been allocated with the descriptor. */
825 (*ppsaOut)->fFeatures &= ~FADF_CREATEVECTOR;
827 /* Get the allocated memory size for source and allocate it for target */
828 ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
829 dAllocSize = ulWholeArraySize*psa->cbElements;
831 (*ppsaOut)->pvData =
832 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dAllocSize);
833 if( (*ppsaOut)->pvData != NULL) { /* HeapAlloc succeed */
835 if( (hRes=duplicateData(psa, *ppsaOut)) != S_OK) { /* E_OUTOFMEMORY */
836 HeapFree(GetProcessHeap(), 0, (*ppsaOut)->pvData);
837 (*ppsaOut)->pvData = NULL;
838 SafeArrayDestroyDescriptor(*ppsaOut);
839 return hRes;
842 } else { /* failed to allocate or dupplicate... */
843 SafeArrayDestroyDescriptor(*ppsaOut);
844 return E_UNEXPECTED; /* UNDOC error condition */
846 } else { /* failed to allocate mem for descriptor */
847 return E_OUTOFMEMORY; /* UNDOC error condiftion */
850 return S_OK;
853 /************************************************************************
854 * SafeArrayCreateVector (OLEAUT32.411)
855 * Creates a one dimension safearray where the data is next to the
856 * SAFEARRAY structure.
858 SAFEARRAY* WINAPI SafeArrayCreateVector(
859 VARTYPE vt,
860 LONG lLbound,
861 ULONG cElements)
863 SAFEARRAY *psa;
864 BYTE *ptr;
866 TRACE("%d, %ld, %ld\n", vt, lLbound, cElements);
868 /* Validate supported VARTYPE */
869 if ( (vt >= LAST_VARTYPE) ||
870 ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
871 return NULL;
873 /* Allocate memory for the array descriptor and data contiguously */
874 ptr = HeapAlloc( GetProcessHeap(),
875 HEAP_ZERO_MEMORY,
876 (sizeof(GUID)+sizeof(*psa)+(VARTYPE_SIZE[vt]*cElements)));
877 if (!ptr)
878 return NULL;
879 psa = (SAFEARRAY*)(ptr+sizeof(GUID));
881 /* setup data members... */
882 psa->cDims = 1; /* always and forever */
883 psa->fFeatures = getFeatures(vt) | FADF_CREATEVECTOR; /* undocumented flag used by Microsoft */
884 psa->cLocks = 0;
885 psa->pvData = (BYTE*)psa + sizeof(*psa);
886 psa->cbElements = VARTYPE_SIZE[vt];
888 psa->rgsabound[0].cElements = cElements;
889 psa->rgsabound[0].lLbound = lLbound;
891 return(psa);
894 /************************************************************************
895 * SafeArrayRedim (OLEAUT32.40)
896 * Changes the caracteristics of the last dimension of the SafeArray
898 HRESULT WINAPI SafeArrayRedim(
899 SAFEARRAY *psa,
900 SAFEARRAYBOUND *psaboundNew)
902 LONG lDelta; /* hold difference in size */
903 USHORT cDims=1; /* dims counter */
905 if( !validArg(psa) )
906 return E_INVALIDARG;
908 if( psa->cLocks > 0 )
909 return DISP_E_ARRAYISLOCKED;
911 if( psa->fFeatures & FADF_FIXEDSIZE )
912 return E_INVALIDARG;
914 if( SafeArrayLock(psa)==E_UNEXPECTED )
915 return E_UNEXPECTED;/* UNDOC error condition */
917 /* find the delta in number of array spot to apply to the new array */
918 lDelta = psaboundNew->cElements - psa->rgsabound[0].cElements;
919 for(; cDims < psa->cDims; cDims++)
920 /* delta in number of spot implied by modifying the last dimension */
921 lDelta *= psa->rgsabound[cDims].cElements;
923 TRACE("elements=%ld, Lbound=%ld (delta=%ld)\n", psaboundNew->cElements, psaboundNew->lLbound, lDelta);
925 if (lDelta == 0) { ;/* same size, maybe a change of lLbound, just set it */
927 } else /* need to enlarge (lDelta +) reduce (lDelta -) */
928 if(! resizeSafeArray(psa, lDelta))
929 return E_UNEXPECTED; /* UNDOC error condition */
931 /* the only modifyable dimension sits in [0] as the dimensions were reversed
932 at array creation time... */
933 psa->rgsabound[0].cElements = psaboundNew->cElements;
934 psa->rgsabound[0].lLbound = psaboundNew->lLbound;
936 return SafeArrayUnlock(psa);
939 /************************************************************************
940 * NOT WINDOWS API - SafeArray* Utility functions
941 ************************************************************************/
943 /************************************************************************
944 * Used to validate the SAFEARRAY type of arg
946 static BOOL validArg(
947 SAFEARRAY *psa)
949 SAFEARRAYBOUND *sab;
950 LONG psaSize = 0;
951 LONG descSize = 0;
952 LONG fullSize = 0;
955 * Let's check for the null pointer just in case.
957 if (psa == NULL)
958 return FALSE;
960 /* Check whether the size of the chunk makes sense... That's the only thing
961 I can think of now... */
963 psaSize = HeapSize(GetProcessHeap(), 0, ((IID*)psa)-1);
964 if (psaSize == -1)
965 /* uh, foreign heap. Better don't mess with it ! */
966 return TRUE;
968 /* size of the descriptor when the SA is not created with CreateVector */
969 descSize = sizeof(GUID) + sizeof(*psa) + (sizeof(*sab) * (psa->cDims-1));
971 /* size of the descriptor + data when created with CreateVector */
972 fullSize = sizeof(*psa) + (psa->cbElements * psa->rgsabound[0].cElements);
974 return((psaSize >= descSize) || (psaSize >= fullSize));
977 /************************************************************************
978 * Used to reallocate memory
980 static BOOL resizeSafeArray(
981 SAFEARRAY *psa,
982 LONG lDelta)
984 ULONG ulWholeArraySize; /* use as multiplicator */
985 PVOID pvNewBlock = NULL;
986 IUnknown *punk;
987 BSTR bstr;
989 ulWholeArraySize = getArraySize(psa);
991 if(lDelta < 0) { /* array needs to be shorthen */
992 if( isPointer(psa->fFeatures)) /* ptr that need to be released */
993 for(;lDelta < 0; lDelta++) {
994 punk = *(IUnknown**)
995 ((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
997 if( punk != NULL )
998 IUnknown_Release(punk);
1001 else if(psa->fFeatures & FADF_BSTR) /* BSTR that need to be freed */
1002 for(;lDelta < 0; lDelta++) {
1003 bstr = *(BSTR*)
1004 ((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
1006 if( bstr != NULL )
1007 SysFreeString( bstr );
1009 else if(psa->fFeatures & FADF_VARIANT)
1010 for(;lDelta < 0; lDelta++) {
1011 VariantClear((VARIANT*)((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements)));
1015 if (!(psa->fFeatures & FADF_CREATEVECTOR))
1017 /* Ok now, if we are enlarging the array, we *MUST* move the whole block
1018 pointed to by pvData. If we are shorthening the array, this move is
1019 optional but we do it anyway becuase the benefit is that we are
1020 releasing to the system the unused memory */
1022 if((pvNewBlock = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, psa->pvData,
1023 (ulWholeArraySize + lDelta) * psa->cbElements)) == NULL)
1024 return FALSE; /* TODO If we get here it means:
1025 SHRINK situation : we've deleted the undesired
1026 data and did not release the memory
1027 GROWING situation: we've been unable to grow the array
1030 else
1032 /* Allocate a new block, because the previous data has been allocated with
1033 the descriptor in SafeArrayCreateVector function. */
1035 if((pvNewBlock = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1036 ulWholeArraySize * psa->cbElements)) == NULL)
1037 return FALSE;
1039 psa->fFeatures &= ~FADF_CREATEVECTOR;
1041 /* reassign to the new block of data */
1042 psa->pvData = pvNewBlock;
1043 return TRUE;
1046 /************************************************************************
1047 * Used to set the fFeatures data member of the SAFEARRAY structure.
1049 static INT getFeatures(VARTYPE vt) {
1050 switch (vt) {
1051 case VT_BSTR: return FADF_BSTR;
1052 case VT_UNKNOWN: return FADF_UNKNOWN;
1053 case VT_DISPATCH: return FADF_DISPATCH;
1054 case VT_VARIANT: return FADF_VARIANT;
1056 return 0;
1059 /************************************************************************
1060 * Used to figure out if the fFeatures data member of the SAFEARRAY
1061 * structure contain any information about the type of data stored...
1063 static BOOL isPointer(
1064 USHORT feature)
1066 switch(feature) {
1067 case FADF_UNKNOWN: return TRUE; /* those are pointers */
1068 case FADF_DISPATCH: return TRUE;
1070 return FALSE;
1073 /************************************************************************
1074 * Used to calculate the displacement when accessing or modifying
1075 * safearray data set.
1077 * Parameters: - LONG *coor is the desired location in the multidimension
1078 * table. Ex for a 3 dim table: coor[] = {1,2,3};
1079 * - ULONG *mat is the format of the table. Ex for a 3 dim
1080 * table mat[] = {4,4,4};
1081 * - USHORT dim is the number of dimension of the SafeArray
1083 static ULONG calcDisplacement(
1084 LONG *coor,
1085 SAFEARRAYBOUND *mat,
1086 LONG dim)
1088 ULONG res = 0;
1089 LONG iterDim;
1091 for(iterDim=0; iterDim<dim; iterDim++)
1092 /* the -mat[dim] bring coor[dim] relative to 0 for calculation */
1093 res += ((coor[iterDim]-mat[iterDim].lLbound) *
1094 endOfDim(coor, mat, iterDim+1, dim));
1096 TRACE("SafeArray: calculated displacement is %lu.\n", res);
1097 return(res);
1100 /************************************************************************
1101 * Recursivity agent for calcDisplacement method. Used within Put and
1102 * Get methods.
1104 static INT endOfDim(
1105 LONG *coor,
1106 SAFEARRAYBOUND *mat,
1107 LONG dim,
1108 LONG realDim)
1110 if(dim==realDim)
1111 return 1;
1112 else
1113 return (endOfDim(coor, mat, dim+1, realDim) * mat[dim].cElements);
1117 /************************************************************************
1118 * Method used to validate the coordinate received in Put and Get
1119 * methods.
1121 static BOOL validCoordinate(
1122 LONG *coor,
1123 SAFEARRAY *psa)
1125 INT iter=0;
1126 LONG lUBound;
1127 LONG lLBound;
1128 HRESULT hRes;
1130 if (!psa->cDims) { FIXME("no dims?\n");return FALSE; }
1131 for(; iter<psa->cDims; iter++) {
1132 TRACE("coor[%d]=%ld\n", iter, coor[iter]);
1133 if((hRes = SafeArrayGetLBound(psa, (iter+1), &lLBound)) != S_OK) {
1134 FIXME("No lbound?\n");
1135 return FALSE;
1137 if((hRes = SafeArrayGetUBound(psa, (iter+1), &lUBound)) != S_OK) {
1138 FIXME("No ubound?\n");
1139 return FALSE;
1141 if(lLBound > lUBound) {
1142 FIXME("lbound larger than ubound?\n");
1143 return FALSE;
1146 if((coor[iter] < lLBound) || (coor[iter] > lUBound)) {
1147 FIXME("coordinate %ld not within %ld - %ld\n",coor[iter], lLBound, lUBound);
1148 return FALSE;
1151 return TRUE;
1154 /************************************************************************
1155 * Method used to calculate the number of cells of the SA
1157 static ULONG getArraySize(
1158 SAFEARRAY *psa)
1160 USHORT cCount;
1161 ULONG ulWholeArraySize = 1;
1163 for(cCount=0; cCount < psa->cDims; cCount++) /* foreach dimensions... */
1164 ulWholeArraySize *= psa->rgsabound[cCount].cElements;
1166 return ulWholeArraySize;
1170 /************************************************************************
1171 * Method used to handle data space dupplication for Copy32 and CopyData32
1173 static HRESULT duplicateData(
1174 SAFEARRAY *psa,
1175 SAFEARRAY *ppsaOut)
1177 ULONG ulWholeArraySize; /* size of the thing */
1178 LONG lDelta;
1180 ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
1182 SafeArrayLock(ppsaOut);
1184 if( isPointer(psa->fFeatures) ) { /* If datatype is object increment
1185 object's reference count */
1186 IUnknown *punk;
1188 for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1189 punk = *(IUnknown**)((char *) psa->pvData+(lDelta * psa->cbElements));
1191 if( punk != NULL)
1192 IUnknown_AddRef(punk);
1195 /* Copy the source array data into target array */
1196 memcpy(ppsaOut->pvData, psa->pvData, ulWholeArraySize*psa->cbElements);
1199 else if( psa->fFeatures & FADF_BSTR ) { /* if datatype is BSTR allocate
1200 the BSTR in the new array */
1201 BSTR pbstrReAllocStr = NULL;
1203 for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1204 if(( pbstrReAllocStr = SYSDUPSTRING(
1205 *(BSTR*)((char *) psa->pvData+(lDelta * psa->cbElements)))) == NULL) {
1207 SafeArrayUnlock(ppsaOut);
1208 return E_OUTOFMEMORY;
1211 *((BSTR*)((char *)ppsaOut->pvData+(lDelta * psa->cbElements))) =
1212 pbstrReAllocStr;
1216 else if( psa->fFeatures & FADF_VARIANT ) {
1218 for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1219 VariantCopy((VARIANT*)((char *) ppsaOut->pvData+(lDelta * psa->cbElements)),
1220 (VARIANT*)((char *) psa->pvData+(lDelta * psa->cbElements)));
1223 } else { /* Simply copy the source array data into target array */
1224 memcpy(ppsaOut->pvData, psa->pvData, ulWholeArraySize*psa->cbElements);
1226 SafeArrayUnlock(ppsaOut);
1227 return S_OK;
1231 /************************************************************************
1232 * SafeArrayGetVartype (OLEAUT32.77)
1233 * Returns the VARTYPE stored in the given safearray
1235 HRESULT WINAPI SafeArrayGetVartype(
1236 SAFEARRAY* psa,
1237 VARTYPE* pvt)
1239 if (psa->fFeatures & FADF_HAVEVARTYPE)
1241 /* VT tag @ negative offset 4 in the array descriptor */
1242 *pvt = ((DWORD*)psa)[-1];
1243 return S_OK;
1246 if (psa->fFeatures & FADF_RECORD)
1248 *pvt = VT_RECORD;
1249 return S_OK;
1252 if (psa->fFeatures & FADF_BSTR)
1254 *pvt = VT_BSTR;
1255 return S_OK;
1258 if (psa->fFeatures & FADF_UNKNOWN)
1260 *pvt = VT_UNKNOWN;
1261 return S_OK;
1264 if (psa->fFeatures & FADF_DISPATCH)
1266 *pvt = VT_UNKNOWN; /* Yes, checked against windows */
1267 return S_OK;
1270 if (psa->fFeatures & FADF_VARIANT)
1272 *pvt = VT_VARIANT;
1273 return S_OK;
1275 if (psa->fFeatures & FADF_HAVEIID)
1277 /* We could check the IID here, but Windows apparently does not
1278 * do that and returns VT_UNKNOWN for VT_DISPATCH too.
1280 *pvt = VT_UNKNOWN;
1281 return S_OK;
1284 WARN("No vt found for safearray\n");
1285 return E_INVALIDARG;
1288 /************************************************************************
1289 * SafeArraySetIID (OLEAUT32.57)
1291 HRESULT WINAPI SafeArraySetIID(SAFEARRAY *arr, REFIID riid) {
1292 IID *xiid = ((IID*)arr)-1;
1293 TRACE("(%p, %s).\n",arr,debugstr_guid(riid));
1295 if (!arr || !(arr->fFeatures & FADF_HAVEIID))
1296 return E_INVALIDARG;
1297 memcpy(xiid, riid, sizeof(GUID));
1298 return S_OK;
1301 /************************************************************************
1302 * SafeArrayGetIID (OLEAUT32.67)
1304 HRESULT WINAPI SafeArrayGetIID(SAFEARRAY *arr, IID *riid) {
1305 IID *xiid = ((IID*)arr)-1;
1306 TRACE("(%p, %s).\n",arr,debugstr_guid(riid));
1308 if (!arr || !(arr->fFeatures & FADF_HAVEIID))
1309 return E_INVALIDARG;
1310 memcpy(riid, xiid, sizeof(GUID));
1311 return S_OK;
1314 /************************************************************************
1315 * SafeArraySetRecordInfo (OLEAUT32.44)
1317 HRESULT WINAPI SafeArraySetRecordInfo(SAFEARRAY *arr, IRecordInfo *iface) {
1318 LPRECORDINFO oldiface;
1320 if (!arr || !(arr->fFeatures & FADF_RECORD))
1321 return E_INVALIDARG;
1322 oldiface = ((IRecordInfo**)arr)[-1];
1323 if (oldiface)
1324 IRecordInfo_Release(oldiface);
1325 ((IRecordInfo**)arr)[-1] = iface;
1326 if (iface)
1327 IRecordInfo_AddRef(iface);
1328 return S_OK;
1331 /************************************************************************
1332 * SafeArrayGetRecordInfo (OLEAUT32.45)
1334 HRESULT WINAPI SafeArrayGetRecordInfo(SAFEARRAY *arr, IRecordInfo** iface) {
1335 if (!arr || !(arr->fFeatures & FADF_RECORD))
1336 return E_INVALIDARG;
1337 *iface = ((IRecordInfo**)arr)[-1];
1338 if (*iface)
1339 IRecordInfo_AddRef(*iface);
1340 return S_OK;