Fixed header dependencies to be fully compatible with the Windows
[wine] / dlls / oleaut32 / safearray.c
1 /*************************************************************************
2  * OLE Automation
3  * SafeArray Implementation
4  *
5  * This file contains the implementation of the SafeArray interface.
6  *
7  * Copyright 1999 Sylvain St-Germain
8  *
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.
13  *
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.
18  *
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
22  */
23 /* Memory Layout of a SafeArray:
24  *
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...]
31  */
32
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include "windef.h"
37 #include "winerror.h"
38 #include "winbase.h"
39 #include "oleauto.h"
40 #include "wine/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(ole);
43
44 #define SYSDUPSTRING(str) SysAllocStringLen((str), SysStringLen(str))
45
46 /* Locally used methods */
47 static ULONG
48 calcDisplacement(LONG *coor, SAFEARRAYBOUND *mat, LONG dim);
49
50 static BOOL
51 isPointer(USHORT feature);
52
53 static INT
54 getFeatures(VARTYPE vt);
55
56 static BOOL
57 validCoordinate(LONG *coor, SAFEARRAY *psa);
58
59 static BOOL
60 resizeSafeArray(SAFEARRAY *psa, LONG lDelta);
61
62 static BOOL
63 validArg(SAFEARRAY *psa);
64
65 static ULONG
66 getArraySize(SAFEARRAY *psa);
67
68 static HRESULT
69 duplicateData(SAFEARRAY *psa, SAFEARRAY *ppsaOut);
70
71 /* Association between VARTYPE and their size.
72    A size of zero is defined for the unsupported types.  */
73
74 #define VARTYPE_NOT_SUPPORTED 0
75 static const ULONG VARTYPE_SIZE[] =
76 {
77   /* this is taken from wtypes.h.  Only [S]es are supported by the SafeArray */
78 VARTYPE_NOT_SUPPORTED,  /* VT_EMPTY    [V]   [P]    nothing                     */
79 VARTYPE_NOT_SUPPORTED,  /* VT_NULL     [V]   [P]    SQL style Nul       */
80 2,                      /* VT_I2       [V][T][P][S] 2 byte signed int */
81 4,                      /* VT_I4       [V][T][P][S] 4 byte signed int */
82 4,                      /* VT_R4       [V][T][P][S] 4 byte real */
83 8,                      /* VT_R8       [V][T][P][S] 8 byte real */
84 8,                      /* VT_CY       [V][T][P][S] currency */
85 8,                      /* VT_DATE     [V][T][P][S] date */
86 sizeof(BSTR),           /* VT_BSTR     [V][T][P][S] OLE Automation string*/
87 sizeof(LPDISPATCH),     /* VT_DISPATCH [V][T][P][S] IDispatch * */
88 4,                      /* VT_ERROR    [V][T]   [S] SCODE       */
89 2,                      /* VT_BOOL     [V][T][P][S] True=-1, False=0*/
90 sizeof(VARIANT),        /* VT_VARIANT  [V][T][P][S] VARIANT *   */
91 sizeof(LPUNKNOWN),      /* VT_UNKNOWN  [V][T]   [S] IUnknown * */
92 sizeof(DECIMAL),        /* VT_DECIMAL  [V][T]   [S] 16 byte fixed point */
93 VARTYPE_NOT_SUPPORTED,                         /* no VARTYPE here.....  */
94 1,                      /* VT_I1          [T]   [S] signed char         */
95 1,                      /* VT_UI1      [V][T][P][S] unsigned char       */
96 2,                      /* VT_UI2         [T][P][S] unsigned short      */
97 4,                      /* VT_UI4         [T][P][S] unsigned int        */
98 VARTYPE_NOT_SUPPORTED,  /* VT_I8          [T][P]    signed 64-bit int                   */
99 VARTYPE_NOT_SUPPORTED,  /* VT_UI8         [T][P]    unsigned 64-bit int         */
100 sizeof(INT),            /* VT_INT         [T]       signed machine int          */
101 sizeof(UINT),           /* VT_UINT        [T]       unsigned machine int        */
102 VARTYPE_NOT_SUPPORTED,  /* VT_VOID        [T]       C style void                        */
103 VARTYPE_NOT_SUPPORTED,  /* VT_HRESULT     [T]       Standard return type        */
104 VARTYPE_NOT_SUPPORTED,  /* VT_PTR         [T]       pointer type                        */
105 VARTYPE_NOT_SUPPORTED,  /* VT_SAFEARRAY   [T]       (use VT_ARRAY in VARIANT)*/
106 VARTYPE_NOT_SUPPORTED,  /* VT_CARRAY      [T]       C style array                       */
107 VARTYPE_NOT_SUPPORTED,  /* VT_USERDEFINED [T]       user defined type                   */
108 VARTYPE_NOT_SUPPORTED,  /* VT_LPSTR       [T][P]    null terminated string      */
109 VARTYPE_NOT_SUPPORTED,  /* VT_LPWSTR      [T][P]    wide null term string               */
110 VARTYPE_NOT_SUPPORTED,  /* 32 */
111 VARTYPE_NOT_SUPPORTED,  /* 33 */
112 VARTYPE_NOT_SUPPORTED,  /* 34 */
113 VARTYPE_NOT_SUPPORTED,  /* 35 */
114 VARTYPE_NOT_SUPPORTED,  /* VT_RECORD                record */
115 VARTYPE_NOT_SUPPORTED,  /* 37 */
116 VARTYPE_NOT_SUPPORTED,  /* 38 */
117 VARTYPE_NOT_SUPPORTED,  /* 39 */
118 VARTYPE_NOT_SUPPORTED,  /* 40 */
119 VARTYPE_NOT_SUPPORTED,  /* 41 */
120 VARTYPE_NOT_SUPPORTED,  /* 42 */
121 VARTYPE_NOT_SUPPORTED,  /* 43 */
122 VARTYPE_NOT_SUPPORTED,  /* 44 */
123 VARTYPE_NOT_SUPPORTED,  /* 45 */
124 VARTYPE_NOT_SUPPORTED,  /* 46 */
125 VARTYPE_NOT_SUPPORTED,  /* 47 */
126 VARTYPE_NOT_SUPPORTED,  /* 48 */
127 VARTYPE_NOT_SUPPORTED,  /* 49 */
128 VARTYPE_NOT_SUPPORTED,  /* 50 */
129 VARTYPE_NOT_SUPPORTED,  /* 51 */
130 VARTYPE_NOT_SUPPORTED,  /* 52 */
131 VARTYPE_NOT_SUPPORTED,  /* 53 */
132 VARTYPE_NOT_SUPPORTED,  /* 54 */
133 VARTYPE_NOT_SUPPORTED,  /* 55 */
134 VARTYPE_NOT_SUPPORTED,  /* 56 */
135 VARTYPE_NOT_SUPPORTED,  /* 57 */
136 VARTYPE_NOT_SUPPORTED,  /* 58 */
137 VARTYPE_NOT_SUPPORTED,  /* 59 */
138 VARTYPE_NOT_SUPPORTED,  /* 60 */
139 VARTYPE_NOT_SUPPORTED,  /* 61 */
140 VARTYPE_NOT_SUPPORTED,  /* 62 */
141 VARTYPE_NOT_SUPPORTED,  /* 63 */
142 VARTYPE_NOT_SUPPORTED,  /* VT_FILETIME       [P]    FILETIME                    */
143 VARTYPE_NOT_SUPPORTED,  /* VT_BLOB           [P]    Length prefixed bytes */
144 VARTYPE_NOT_SUPPORTED,  /* VT_STREAM         [P]    Name of stream follows              */
145 VARTYPE_NOT_SUPPORTED,  /* VT_STORAGE        [P]    Name of storage follows     */
146 VARTYPE_NOT_SUPPORTED,  /* VT_STREAMED_OBJECT[P]    Stream contains an object*/
147 VARTYPE_NOT_SUPPORTED,  /* VT_STORED_OBJECT  [P]    Storage contains object*/
148 VARTYPE_NOT_SUPPORTED,  /* VT_BLOB_OBJECT    [P]    Blob contains an object*/
149 VARTYPE_NOT_SUPPORTED,  /* VT_CF             [P]    Clipboard format                    */
150 VARTYPE_NOT_SUPPORTED,  /* VT_CLSID          [P]    A Class ID                  */
151 };
152
153 static const int LAST_VARTYPE = sizeof(VARTYPE_SIZE)/sizeof(VARTYPE_SIZE[0]);
154
155
156 /*************************************************************************
157  *              SafeArrayAllocDescriptor (OLEAUT32.36)
158  * Allocate the appropriate amount of memory for the SafeArray descriptor
159  */
160 HRESULT WINAPI SafeArrayAllocDescriptor(
161   UINT    cDims,
162   SAFEARRAY **ppsaOut)
163 {
164   SAFEARRAYBOUND *sab;
165   LONG allocSize = 0;
166   char *ptr;
167
168   if (!cDims || cDims >= 0x10000) /* 65536 appears to be the limit */
169     return E_INVALIDARG;
170   if (!ppsaOut)
171     return E_POINTER;
172
173   /* GUID + SAFEARRAY + SAFEARRAYBOUND * (cDims -1)
174    * ( -1 because there is already one ( in SAFEARRAY struct
175    */
176   allocSize = sizeof(GUID) + sizeof(**ppsaOut) + (sizeof(*sab) * (cDims-1));
177
178   /* Allocate memory for SAFEARRAY struc */
179   ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, allocSize);
180   if (!ptr)
181     return E_OUTOFMEMORY;
182   *ppsaOut = (SAFEARRAY *)(ptr + sizeof(GUID));
183   (*ppsaOut)->cDims     = cDims;
184   TRACE("(%d): %lu bytes allocated for descriptor.\n", cDims, allocSize);
185
186   return(S_OK);
187 }
188
189 /*************************************************************************
190  *              SafeArrayAllocDescriptorEx (OLEAUT32.41)
191  * Allocate the appropriate amount of memory for the SafeArray descriptor
192  * and also store information about the vartype before the returned pointer.
193  */
194 HRESULT WINAPI SafeArrayAllocDescriptorEx(
195   VARTYPE vt,
196   UINT    cDims,
197   SAFEARRAY **ppsaOut)
198 {
199   HRESULT hres;
200
201   hres = SafeArrayAllocDescriptor (cDims, ppsaOut);
202   if (FAILED(hres))
203     return hres;
204
205   switch (vt) {
206   case VT_DISPATCH:
207     (*ppsaOut)->fFeatures = FADF_HAVEIID;
208     SafeArraySetIID( *ppsaOut, &IID_IDispatch);
209     break;
210   case VT_UNKNOWN:
211     (*ppsaOut)->fFeatures = FADF_HAVEIID;
212     SafeArraySetIID( *ppsaOut, &IID_IUnknown);
213     break;
214   case VT_RECORD:
215     (*ppsaOut)->fFeatures = FADF_RECORD;
216     break;
217   default:
218     (*ppsaOut)->fFeatures = FADF_HAVEVARTYPE;
219     ((DWORD*)*ppsaOut)[-1] = vt;
220     break;
221   }
222   return S_OK;
223 }
224
225 /*************************************************************************
226  *              SafeArrayAllocData (OLEAUT32.37)
227  * Allocate the appropriate amount of data for the SafeArray data
228  */
229 HRESULT WINAPI SafeArrayAllocData(
230   SAFEARRAY *psa)
231 {
232   ULONG  ulWholeArraySize;   /* to store the size of the whole thing */
233
234   if(! validArg(psa))
235     return E_INVALIDARG;
236
237   ulWholeArraySize = getArraySize(psa);
238
239   /* Allocate memory for the data itself */
240   if((psa->pvData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
241         psa->cbElements*ulWholeArraySize)) == NULL)
242     return(E_UNEXPECTED);
243
244   TRACE("SafeArray: %lu bytes allocated for data at %p (%lu objects).\n",
245     psa->cbElements*ulWholeArraySize, psa->pvData, ulWholeArraySize);
246
247   return(S_OK);
248 }
249
250 /*************************************************************************
251  *              SafeArrayCreate (OLEAUT32.15)
252  * Create a SafeArray object by encapsulating AllocDescriptor and AllocData
253  */
254 SAFEARRAY* WINAPI SafeArrayCreate(
255   VARTYPE        vt,
256   UINT         cDims,
257   SAFEARRAYBOUND *rgsabound)
258 {
259   SAFEARRAY *psa;
260   HRESULT   hRes;
261   USHORT    cDim;
262
263   TRACE("(%d, %d, %p)\n", vt, cDims, rgsabound);
264
265   /* Validate supported VARTYPE */
266   if ( (vt >= LAST_VARTYPE) ||
267        ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
268     return NULL;
269
270   /* Allocate memory for the array descriptor */
271   if( FAILED( hRes = SafeArrayAllocDescriptorEx(vt, cDims, &psa)))
272     return NULL;
273
274   /* setup data members... */
275   psa->cDims     = cDims;
276   switch (vt) {
277   case VT_BSTR:      psa->fFeatures |= FADF_BSTR;break;
278   case VT_UNKNOWN:   psa->fFeatures |= FADF_UNKNOWN;break;
279   case VT_DISPATCH:  psa->fFeatures |= FADF_DISPATCH;break;
280   case VT_VARIANT:   psa->fFeatures |= FADF_VARIANT;break;
281   default: break;
282   }
283   psa->cLocks    = 0;
284   psa->pvData    = NULL;
285   psa->cbElements= VARTYPE_SIZE[vt];
286
287   /* Invert the bounds ... */
288   for(cDim=0; cDim < psa->cDims; cDim++) {
289     psa->rgsabound[cDim].cElements = rgsabound[psa->cDims-cDim-1].cElements;
290     psa->rgsabound[cDim].lLbound   = rgsabound[psa->cDims-cDim-1].lLbound;
291   }
292
293   /* allocate memory for the data... */
294   if( FAILED( hRes = SafeArrayAllocData(psa))) {
295     SafeArrayDestroyDescriptor(psa);
296     ERR("() : Failed to allocate the Safe Array data\n");
297     return NULL;
298   }
299
300   return(psa);
301 }
302
303 /*************************************************************************
304  *              SafeArrayDestroyDescriptor (OLEAUT32.38)
305  * Frees the memory associated with the descriptor.
306  */
307 HRESULT WINAPI SafeArrayDestroyDescriptor(
308   SAFEARRAY *psa)
309 {
310   LPVOID ptr;
311
312   /* Check for lockness before to free... */
313   if(psa->cLocks > 0)
314     return DISP_E_ARRAYISLOCKED;
315
316   /* The array is unlocked, then, deallocate memory */
317   ptr = ((IID*)psa)-1;
318   if(HeapFree( GetProcessHeap(), 0, ptr) == FALSE)
319     return E_UNEXPECTED;
320   return(S_OK);
321 }
322
323
324 /*************************************************************************
325  *              SafeArrayLock (OLEAUT32.21)
326  * Increment the lock counter
327  *
328  * Doc says (MSDN Library ) that psa->pvData should be made available (!= NULL)
329  * only when psa->cLocks is > 0... I don't get it since pvData is allocated
330  * before the array is locked, therefore
331  */
332 HRESULT WINAPI SafeArrayLock(
333   SAFEARRAY *psa)
334 {
335   if(! validArg(psa))
336     return E_INVALIDARG;
337
338   psa->cLocks++;
339
340   return(S_OK);
341 }
342
343 /*************************************************************************
344  *              SafeArrayUnlock (OLEAUT32.22)
345  * Decrement the lock counter
346  */
347 HRESULT WINAPI SafeArrayUnlock(
348   SAFEARRAY *psa)
349 {
350   if(! validArg(psa))
351     return E_INVALIDARG;
352
353   if (psa->cLocks > 0)
354     psa->cLocks--;
355
356   return(S_OK);
357 }
358
359
360 /*************************************************************************
361  *              SafeArrayPutElement (OLEAUT32.26)
362  * Set the data at the given coordinate
363  */
364 HRESULT WINAPI SafeArrayPutElement(
365   SAFEARRAY *psa,
366   LONG      *rgIndices,
367   void      *pv)
368 {
369   ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to
370                                          the desired one... */
371   PVOID elementStorageAddress = NULL; /* Address to store the data */
372
373   /* Validate the index given */
374   if(! validCoordinate(rgIndices, psa))
375     return DISP_E_BADINDEX;
376   if(! validArg(psa))
377     return E_INVALIDARG;
378
379   if( SafeArrayLock(psa) == S_OK) {
380
381     /* Figure out the number of items to skip */
382     stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
383
384     /* Figure out the number of byte to skip ... */
385     elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
386
387     if(isPointer(psa->fFeatures)) { /* increment ref count for this pointer */
388
389       *((PVOID*)elementStorageAddress) = *(PVOID*)pv;
390       IUnknown_AddRef( *(IUnknown**)pv);
391
392     } else {
393
394       if(psa->fFeatures & FADF_BSTR) { /* Create a new object */
395         BSTR pbstrReAllocStr = NULL;
396         if(pv &&
397            ((pbstrReAllocStr = SYSDUPSTRING( (OLECHAR*)pv )) == NULL)) {
398           SafeArrayUnlock(psa);
399           return E_OUTOFMEMORY;
400         } else
401           *((BSTR*)elementStorageAddress) = pbstrReAllocStr;
402       }
403       else if(psa->fFeatures & FADF_VARIANT) {
404         HRESULT hr = VariantCopy(elementStorageAddress, pv);
405         if (FAILED(hr)) {
406           SafeArrayUnlock(psa);
407           return hr;
408         }
409       }
410       else /* duplicate the memory */
411         memcpy(elementStorageAddress, pv, SafeArrayGetElemsize(psa) );
412     }
413
414   } else {
415     ERR("SafeArray: Cannot lock array....\n");
416     return E_UNEXPECTED; /* UNDOC error condition */
417   }
418
419   TRACE("SafeArray: item put at address %p.\n",elementStorageAddress);
420   return SafeArrayUnlock(psa);
421 }
422
423
424 /*************************************************************************
425  *              SafeArrayGetElement (OLEAUT32.25)
426  * Return the data element corresponding the the given coordinate
427  */
428 HRESULT WINAPI SafeArrayGetElement(
429   SAFEARRAY *psa,
430   LONG      *rgIndices,
431   void      *pv)
432 {
433   ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to
434                                          the desired one... */
435   PVOID elementStorageAddress = NULL; /* Address to store the data */
436
437   if(! validArg(psa))
438     return E_INVALIDARG;
439
440   if(! validCoordinate(rgIndices, psa)) /* Validate the index given */
441     return(DISP_E_BADINDEX);
442
443   if( SafeArrayLock(psa) == S_OK) {
444
445     /* Figure out the number of items to skip */
446     stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
447
448     /* Figure out the number of byte to skip ... */
449     elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
450
451     if( psa->fFeatures & FADF_BSTR) {           /* reallocate the obj */
452       BSTR pbstrStoredStr = *(OLECHAR**)elementStorageAddress;
453       BSTR pbstrReturnedStr = NULL;
454       if( pbstrStoredStr &&
455           ((pbstrReturnedStr = SYSDUPSTRING( pbstrStoredStr )) == NULL) ) {
456         SafeArrayUnlock(psa);
457         return E_OUTOFMEMORY;
458       } else
459         *((BSTR*)pv) = pbstrReturnedStr;
460     }
461     else if( psa->fFeatures & FADF_VARIANT) {
462       HRESULT hr;
463       VariantInit(pv);
464       hr = VariantCopy(pv, elementStorageAddress);
465       if (FAILED(hr)) {
466         SafeArrayUnlock(psa);
467         return hr;
468       }
469     }
470     else if( isPointer(psa->fFeatures) )         /* simply copy the pointer */
471       *(PVOID*)pv = *((PVOID*)elementStorageAddress);
472     else                                         /* copy the bytes */
473       memcpy(pv, elementStorageAddress, psa->cbElements );
474
475   } else {
476     ERR("SafeArray: Cannot lock array....\n");
477     return E_UNEXPECTED; /* UNDOC error condition */
478   }
479
480   return( SafeArrayUnlock(psa) );
481 }
482
483 /*************************************************************************
484  *              SafeArrayGetUBound (OLEAUT32.19)
485  * return the UP bound for a given array dimension
486  * Note: [0] is the right most (least significant) array index!
487  */
488 HRESULT WINAPI SafeArrayGetUBound(
489   SAFEARRAY *psa,
490   UINT    nDim,
491   LONG      *plUbound)
492 {
493   if(! validArg(psa))
494     return E_INVALIDARG;
495
496   if(nDim > psa->cDims)
497     return DISP_E_BADINDEX;
498
499   if(0 == nDim)
500     return DISP_E_BADINDEX;
501
502   *plUbound = psa->rgsabound[psa->cDims - nDim].lLbound +
503               psa->rgsabound[psa->cDims - nDim].cElements - 1;
504
505   return S_OK;
506 }
507
508 /*************************************************************************
509  *              SafeArrayGetLBound (OLEAUT32.20)
510  * Return the LO bound for a given array dimension
511  * Note: [0] is the right most (least significant) array index!
512  */
513 HRESULT WINAPI SafeArrayGetLBound(
514   SAFEARRAY *psa,
515   UINT    nDim,
516   LONG      *plLbound)
517 {
518   if(! validArg(psa))
519     return E_INVALIDARG;
520
521   if(nDim > psa->cDims)
522     return DISP_E_BADINDEX;
523
524   if(0 == nDim)
525     return DISP_E_BADINDEX;
526
527   *plLbound = psa->rgsabound[psa->cDims - nDim].lLbound;
528   return S_OK;
529 }
530
531 /*************************************************************************
532  *              SafeArrayGetDim (OLEAUT32.17)
533  * returns the number of dimension in the array
534  */
535 UINT WINAPI SafeArrayGetDim(
536   SAFEARRAY * psa)
537 {
538   /*
539    * A quick test in Windows shows that the behavior here for an invalid
540    * pointer is to return 0.
541    */
542   if(! validArg(psa))
543     return 0;
544
545   return psa->cDims;
546 }
547
548 /*************************************************************************
549  *              SafeArrayGetElemsize (OLEAUT32.18)
550  * Return the size of the element in the array
551  */
552 UINT WINAPI SafeArrayGetElemsize(
553   SAFEARRAY * psa)
554 {
555   /*
556    * A quick test in Windows shows that the behavior here for an invalid
557    * pointer is to return 0.
558    */
559   if(! validArg(psa))
560     return 0;
561
562   return psa->cbElements;
563 }
564
565 /*************************************************************************
566  *              SafeArrayAccessData (OLEAUT32.23)
567  * increment the access count and return the data
568  */
569 HRESULT WINAPI SafeArrayAccessData(
570   SAFEARRAY *psa,
571   void      **ppvData)
572 {
573   HRESULT hRes;
574
575   if(! validArg(psa))
576     return E_INVALIDARG;
577
578   hRes = SafeArrayLock(psa);
579
580   switch (hRes) {
581     case S_OK:
582       (*ppvData) = psa->pvData;
583       break;
584     case E_INVALIDARG:
585       (*ppvData) = NULL;
586       return E_INVALIDARG;
587   }
588
589   return S_OK;
590 }
591
592
593 /*************************************************************************
594  *              SafeArrayUnaccessData (OLEAUT32.24)
595  * Decrement the access count
596  */
597 HRESULT WINAPI SafeArrayUnaccessData(
598   SAFEARRAY * psa)
599 {
600   if(! validArg(psa))
601     return E_INVALIDARG;
602
603   return(SafeArrayUnlock(psa));
604 }
605
606 /************************************************************************
607  *              SafeArrayPtrOfIndex (OLEAUT32.148)
608  * Return a pointer to the element at rgIndices
609  */
610 HRESULT WINAPI SafeArrayPtrOfIndex(
611   SAFEARRAY *psa,
612   LONG      *rgIndices,
613   void      **ppvData)
614 {
615   ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to
616                                          the desired one... */
617
618   if(! validArg(psa))
619     return E_INVALIDARG;
620
621   if(! validCoordinate(rgIndices, psa))
622     return DISP_E_BADINDEX;
623
624   /* Although it is dangerous to do this without having a lock, it is not
625    * illegal.  Microsoft do warn of the danger.
626    */
627
628   /* Figure out the number of items to skip */
629   stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
630
631   *ppvData = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
632
633   return S_OK;
634 }
635
636 /************************************************************************
637  *              SafeArrayDestroyData (OLEAUT32.39)
638  * Frees the memory data bloc
639  */
640 HRESULT WINAPI SafeArrayDestroyData(
641   SAFEARRAY *psa)
642 {
643   HRESULT  hRes;
644   ULONG    ulWholeArraySize; /* count spot in array  */
645   ULONG    ulDataIter;       /* to iterate the data space */
646
647   if(! validArg(psa))
648     return E_INVALIDARG;
649
650   if(psa->cLocks > 0)
651     return DISP_E_ARRAYISLOCKED;
652
653   if(psa->pvData==NULL)
654     return S_OK;
655
656   ulWholeArraySize = getArraySize(psa);
657
658   if(isPointer(psa->fFeatures)) {           /* release the pointers */
659     IUnknown *punk;
660
661     for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
662       punk = *(IUnknown**)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));
663
664       if( punk != NULL)
665         IUnknown_Release(punk);
666     }
667
668   }
669   else if(psa->fFeatures & FADF_BSTR) {  /* deallocate the obj */
670     BSTR bstr;
671
672     for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
673       bstr = *(BSTR*)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));
674
675       if( bstr != NULL)
676         SysFreeString( bstr );
677     }
678   }
679   else if(psa->fFeatures & FADF_VARIANT) {  /* deallocate the obj */
680
681     for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
682       VariantClear((VARIANT*)((char *) psa->pvData+(ulDataIter*(psa->cbElements))));
683     }
684   }
685
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 */
690
691     /* free the whole chunk */
692     if((hRes = HeapFree( GetProcessHeap(), 0, psa->pvData)) == 0) /*failed*/
693       return E_UNEXPECTED; /* UNDOC error condition */
694
695     psa->pvData = NULL;
696   }
697
698   return S_OK;
699 }
700
701 /************************************************************************
702  *              SafeArrayCopyData (OLEAUT32.412)
703  * Copy the psaSource's data block into psaTarget if dimension and size
704  * permits it.
705  */
706 HRESULT WINAPI SafeArrayCopyData(
707   SAFEARRAY *psaSource,
708   SAFEARRAY *psaTarget)
709 {
710   USHORT   cDimCount;        /* looper */
711   LONG     lDelta;           /* looper */
712   IUnknown *punk;
713   ULONG    ulWholeArraySize; /* Number of item in SA */
714   BSTR   bstr;
715
716   if(! (validArg(psaSource) && validArg(psaTarget)) )
717     return E_INVALIDARG;
718
719   if(SafeArrayGetDim(psaSource) != SafeArrayGetDim(psaTarget))
720     return E_INVALIDARG;
721
722   ulWholeArraySize = getArraySize(psaSource);
723
724   /* The two arrays boundaries must be of same length */
725   for(cDimCount=0;cDimCount < psaSource->cDims; cDimCount++)
726     if( psaSource->rgsabound[cDimCount].cElements !=
727       psaTarget->rgsabound[cDimCount].cElements)
728       return E_INVALIDARG;
729
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));
735
736       if( punk != NULL)
737         IUnknown_Release(punk);
738     }
739
740   }
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));
746
747       if( bstr != NULL)
748         SysFreeString( bstr );
749     }
750   }
751   else if( psaTarget->fFeatures & FADF_VARIANT) {
752
753     for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
754       VariantClear((VARIANT*)((char *) psaTarget->pvData + (lDelta * psaTarget->cbElements)));
755     }
756   }
757
758   return duplicateData(psaSource, psaTarget);
759 }
760
761 /************************************************************************
762  *              SafeArrayDestroy (OLEAUT32.16)
763  * Deallocates all memory reserved for the SafeArray
764  */
765 HRESULT WINAPI SafeArrayDestroy(
766   SAFEARRAY * psa)
767 {
768   HRESULT hRes;
769
770   if(! validArg(psa))
771     return E_INVALIDARG;
772
773   if(psa->cLocks > 0)
774     return DISP_E_ARRAYISLOCKED;
775
776   if((hRes = SafeArrayDestroyData( psa )) == S_OK)
777     if((hRes = SafeArrayDestroyDescriptor( psa )) == S_OK)
778       return S_OK;
779
780   return E_UNEXPECTED; /* UNDOC error condition */
781 }
782
783 /************************************************************************
784  *              SafeArrayCopy (OLEAUT32.27)
785  * Make a dupplicate of a SafeArray
786  */
787 HRESULT WINAPI SafeArrayCopy(
788   SAFEARRAY *psa,
789   SAFEARRAY **ppsaOut)
790 {
791   HRESULT hRes;
792   DWORD   dAllocSize;
793   ULONG   ulWholeArraySize; /* size of the thing */
794
795   if(! validArg(psa))
796     return E_INVALIDARG;
797
798   if((hRes=SafeArrayAllocDescriptor(psa->cDims, ppsaOut)) == S_OK){
799
800     /* Duplicate the SAFEARRAY struct */
801     memcpy(*ppsaOut, psa,
802             sizeof(*psa)+(sizeof(*(psa->rgsabound))*(psa->cDims-1)));
803
804     /* If the features that use storage before the SAFEARRAY struct are
805      * enabled, also copy this memory range. Flags have been copied already.
806      */
807     if (psa->fFeatures & (FADF_HAVEIID | FADF_HAVEVARTYPE))
808       memcpy(((GUID*)*ppsaOut)-1, ((GUID*)psa)-1, sizeof(GUID));
809
810     /* Copy the IRecordInfo* reference */
811     if (psa->fFeatures & FADF_RECORD) {
812       IRecordInfo *ri;
813
814       ri = ((IRecordInfo**)psa)[-1];
815       if (ri) {
816         ((IRecordInfo**)*ppsaOut)[-1] = ri;
817         IRecordInfo_AddRef(ri);
818       }
819     }
820
821     (*ppsaOut)->pvData = NULL; /* do not point to the same data area */
822
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;
826
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;
830
831     (*ppsaOut)->pvData =
832       HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dAllocSize);
833     if( (*ppsaOut)->pvData != NULL) {   /* HeapAlloc succeed */
834
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;
840       }
841
842     } else { /* failed to allocate or dupplicate... */
843       SafeArrayDestroyDescriptor(*ppsaOut);
844       return E_UNEXPECTED; /* UNDOC error condition */
845     }
846   } else { /* failed to allocate mem for descriptor */
847     return E_OUTOFMEMORY; /* UNDOC error condiftion */
848   }
849
850   return S_OK;
851 }
852
853 /************************************************************************
854  *              SafeArrayCreateVector (OLEAUT32.411)
855  * Creates a one dimension safearray where the data is next to the
856  * SAFEARRAY structure.
857  */
858 SAFEARRAY* WINAPI SafeArrayCreateVector(
859   VARTYPE vt,
860   LONG    lLbound,
861   ULONG   cElements)
862 {
863   SAFEARRAY *psa;
864   BYTE *ptr;
865
866   TRACE("%d, %ld, %ld\n", vt, lLbound, cElements);
867
868   /* Validate supported VARTYPE */
869   if ( (vt >= LAST_VARTYPE) ||
870        ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
871     return NULL;
872
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));
880
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];
887
888   psa->rgsabound[0].cElements = cElements;
889   psa->rgsabound[0].lLbound   = lLbound;
890
891   return(psa);
892 }
893
894 /************************************************************************
895  *              SafeArrayRedim (OLEAUT32.40)
896  * Changes the caracteristics of the last dimension of the SafeArray
897  */
898 HRESULT WINAPI SafeArrayRedim(
899   SAFEARRAY      *psa,
900   SAFEARRAYBOUND *psaboundNew)
901 {
902   LONG   lDelta;  /* hold difference in size */
903   USHORT cDims=1; /* dims counter */
904
905   if( !validArg(psa) )
906     return E_INVALIDARG;
907
908   if( psa->cLocks > 0 )
909     return DISP_E_ARRAYISLOCKED;
910
911   if( psa->fFeatures & FADF_FIXEDSIZE )
912     return E_INVALIDARG;
913
914   if( SafeArrayLock(psa)==E_UNEXPECTED )
915     return E_UNEXPECTED;/* UNDOC error condition */
916
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;
922
923   TRACE("elements=%ld, Lbound=%ld (delta=%ld)\n", psaboundNew->cElements, psaboundNew->lLbound, lDelta);
924
925   if (lDelta == 0) { ;/* same size, maybe a change of lLbound, just set it */
926
927   } else /* need to enlarge (lDelta +) reduce (lDelta -) */
928     if(! resizeSafeArray(psa, lDelta))
929       return E_UNEXPECTED; /* UNDOC error condition */
930
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;
935
936   return SafeArrayUnlock(psa);
937 }
938
939 /************************************************************************
940  * NOT WINDOWS API - SafeArray* Utility functions
941  ************************************************************************/
942
943 /************************************************************************
944  * Used to validate the SAFEARRAY type of arg
945  */
946 static BOOL validArg(
947   SAFEARRAY *psa)
948 {
949   SAFEARRAYBOUND *sab;
950   LONG psaSize  = 0;
951   LONG descSize = 0;
952   LONG fullSize = 0;
953
954   /*
955    * Let's check for the null pointer just in case.
956    */
957   if (psa == NULL)
958     return FALSE;
959
960   /* Check whether the size of the chunk makes sense... That's the only thing
961      I can think of now... */
962
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;
967
968   /* size of the descriptor when the SA is not created with CreateVector */
969   descSize = sizeof(GUID) + sizeof(*psa) + (sizeof(*sab) * (psa->cDims-1));
970
971   /* size of the descriptor + data when created with CreateVector */
972   fullSize = sizeof(*psa) + (psa->cbElements * psa->rgsabound[0].cElements);
973
974   return((psaSize >= descSize) || (psaSize >= fullSize));
975 }
976
977 /************************************************************************
978  * Used to reallocate memory
979  */
980 static BOOL resizeSafeArray(
981   SAFEARRAY *psa,
982   LONG lDelta)
983 {
984   ULONG    ulWholeArraySize;  /* use as multiplicator */
985   PVOID    pvNewBlock = NULL;
986   IUnknown *punk;
987   BSTR   bstr;
988
989   ulWholeArraySize = getArraySize(psa);
990
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));
996
997         if( punk != NULL )
998           IUnknown_Release(punk);
999             }
1000
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));
1005
1006         if( bstr != NULL )
1007           SysFreeString( bstr );
1008       }
1009     else if(psa->fFeatures & FADF_VARIANT)
1010       for(;lDelta < 0; lDelta++) {
1011         VariantClear((VARIANT*)((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements)));
1012       }
1013   }
1014
1015   if (!(psa->fFeatures & FADF_CREATEVECTOR))
1016   {
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 */
1021
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
1028                       */
1029   }
1030   else
1031   {
1032     /* Allocate a new block, because the previous data has been allocated with
1033        the descriptor in SafeArrayCreateVector function. */
1034
1035     if((pvNewBlock = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1036        ulWholeArraySize * psa->cbElements)) == NULL)
1037         return FALSE;
1038
1039     psa->fFeatures &= ~FADF_CREATEVECTOR;
1040   }
1041   /* reassign to the new block of data */
1042   psa->pvData = pvNewBlock;
1043   return TRUE;
1044 }
1045
1046 /************************************************************************
1047  * Used to set the fFeatures data member of the SAFEARRAY structure.
1048  */
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;
1055   }
1056   return 0;
1057 }
1058
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...
1062  */
1063 static BOOL isPointer(
1064   USHORT feature)
1065 {
1066   switch(feature) {
1067     case FADF_UNKNOWN:  return TRUE; /* those are pointers */
1068     case FADF_DISPATCH: return TRUE;
1069   }
1070   return FALSE;
1071 }
1072
1073 /************************************************************************
1074  * Used to calculate the displacement when accessing or modifying
1075  * safearray data set.
1076  *
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
1082  */
1083 static ULONG calcDisplacement(
1084   LONG           *coor,
1085   SAFEARRAYBOUND *mat,
1086   LONG           dim)
1087 {
1088   ULONG res = 0;
1089   LONG  iterDim;
1090
1091   TRACE("dims is %ld\n", dim);
1092
1093   for (iterDim = dim-1; iterDim >= 0; iterDim--) {
1094         TRACE("%ld: lbound is %ld, adding %ld\n", iterDim, mat[dim-iterDim-1].lLbound,(coor[iterDim] - mat[dim-iterDim-1].lLbound));
1095         res += (coor[iterDim] - mat[dim-iterDim-1].lLbound);
1096
1097         if (iterDim > 0)
1098                 res *= mat[dim-iterDim].cElements;
1099   }
1100
1101   TRACE("SafeArray: calculated displacement is %lu.\n", res);
1102   return(res);
1103 }
1104
1105 /************************************************************************
1106  * Method used to validate the coordinate received in Put and Get
1107  * methods.
1108  */
1109 static BOOL validCoordinate(
1110   LONG      *coor,
1111   SAFEARRAY *psa)
1112 {
1113   INT   iter=0;
1114   LONG    lUBound;
1115   LONG    lLBound;
1116   HRESULT hRes;
1117
1118   if (!psa->cDims) { FIXME("no dims?\n");return FALSE; }
1119   for(; iter<psa->cDims; iter++) {
1120     TRACE("coor[%d]=%ld\n", iter, coor[iter]);
1121     if((hRes = SafeArrayGetLBound(psa, (iter+1), &lLBound)) != S_OK) {
1122       FIXME("No lbound?\n");
1123       return FALSE;
1124     }
1125     if((hRes = SafeArrayGetUBound(psa, (iter+1), &lUBound)) != S_OK) {
1126       FIXME("No ubound?\n");
1127       return FALSE;
1128     }
1129     if(lLBound > lUBound) {
1130       FIXME("lbound larger than ubound?\n");
1131       return FALSE;
1132     }
1133
1134     if((coor[iter] < lLBound) || (coor[iter] > lUBound)) {
1135       FIXME("coordinate %ld not within %ld - %ld\n",coor[iter], lLBound, lUBound);
1136       return FALSE;
1137     }
1138   }
1139   return TRUE;
1140 }
1141
1142 /************************************************************************
1143  * Method used to calculate the number of cells of the SA
1144  */
1145 static ULONG getArraySize(
1146   SAFEARRAY *psa)
1147 {
1148   USHORT cCount;
1149   ULONG  ulWholeArraySize = 1;
1150
1151   for(cCount=0; cCount < psa->cDims; cCount++) /* foreach dimensions... */
1152     ulWholeArraySize *= psa->rgsabound[cCount].cElements;
1153
1154   return ulWholeArraySize;
1155 }
1156
1157
1158 /************************************************************************
1159  * Method used to handle data space dupplication for Copy32 and CopyData32
1160  */
1161 static HRESULT duplicateData(
1162   SAFEARRAY *psa,
1163   SAFEARRAY *ppsaOut)
1164 {
1165   ULONG    ulWholeArraySize; /* size of the thing */
1166   LONG     lDelta;
1167
1168   ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
1169
1170   SafeArrayLock(ppsaOut);
1171
1172   if( isPointer(psa->fFeatures) ) {  /* If datatype is object increment
1173                                         object's reference count */
1174     IUnknown *punk;
1175
1176     for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1177       punk = *(IUnknown**)((char *) psa->pvData+(lDelta * psa->cbElements));
1178
1179       if( punk != NULL)
1180         IUnknown_AddRef(punk);
1181     }
1182
1183     /* Copy the source array data into target array */
1184     memcpy(ppsaOut->pvData, psa->pvData, ulWholeArraySize*psa->cbElements);
1185
1186   }
1187   else if( psa->fFeatures & FADF_BSTR ) { /* if datatype is BSTR allocate
1188                                              the BSTR in the new array */
1189     BSTR   pbstrReAllocStr = NULL;
1190
1191     for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1192       if(( pbstrReAllocStr = SYSDUPSTRING(
1193             *(BSTR*)((char *) psa->pvData+(lDelta * psa->cbElements)))) == NULL) {
1194
1195         SafeArrayUnlock(ppsaOut);
1196         return E_OUTOFMEMORY;
1197       }
1198
1199       *((BSTR*)((char *)ppsaOut->pvData+(lDelta * psa->cbElements))) =
1200         pbstrReAllocStr;
1201     }
1202
1203   }
1204   else if( psa->fFeatures & FADF_VARIANT ) {
1205
1206     for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1207       VariantCopy((VARIANT*)((char *) ppsaOut->pvData+(lDelta * psa->cbElements)),
1208                   (VARIANT*)((char *) psa->pvData+(lDelta * psa->cbElements)));
1209     }
1210
1211   } else { /* Simply copy the source array data into target array */
1212     memcpy(ppsaOut->pvData, psa->pvData, ulWholeArraySize*psa->cbElements);
1213   }
1214   SafeArrayUnlock(ppsaOut);
1215   return S_OK;
1216 }
1217
1218
1219 /************************************************************************
1220  *              SafeArrayGetVartype (OLEAUT32.77)
1221  * Returns the VARTYPE stored in the given safearray
1222  */
1223 HRESULT WINAPI SafeArrayGetVartype(
1224   SAFEARRAY* psa,
1225   VARTYPE*   pvt)
1226 {
1227   if (psa->fFeatures & FADF_HAVEVARTYPE)
1228   {
1229     /* VT tag @ negative offset 4 in the array descriptor */
1230     *pvt = ((DWORD*)psa)[-1];
1231     return S_OK;
1232   }
1233
1234   if (psa->fFeatures & FADF_RECORD)
1235   {
1236     *pvt = VT_RECORD;
1237     return S_OK;
1238   }
1239
1240   if (psa->fFeatures & FADF_BSTR)
1241   {
1242     *pvt = VT_BSTR;
1243     return S_OK;
1244   }
1245
1246   if (psa->fFeatures & FADF_UNKNOWN)
1247   {
1248     *pvt = VT_UNKNOWN;
1249     return S_OK;
1250   }
1251
1252   if (psa->fFeatures & FADF_DISPATCH)
1253   {
1254     *pvt = VT_UNKNOWN; /* Yes, checked against windows */
1255     return S_OK;
1256   }
1257
1258   if (psa->fFeatures & FADF_VARIANT)
1259   {
1260     *pvt = VT_VARIANT;
1261     return S_OK;
1262   }
1263   if (psa->fFeatures & FADF_HAVEIID)
1264   {
1265     /* We could check the IID here, but Windows apparently does not
1266      * do that and returns VT_UNKNOWN for VT_DISPATCH too.
1267      */
1268     *pvt = VT_UNKNOWN;
1269     return S_OK;
1270   }
1271
1272   WARN("No vt found for safearray\n");
1273   return E_INVALIDARG;
1274 }
1275
1276 /************************************************************************
1277  *              SafeArraySetIID (OLEAUT32.57)
1278  */
1279 HRESULT WINAPI SafeArraySetIID(SAFEARRAY *arr, REFIID riid) {
1280   IID *xiid = ((IID*)arr)-1;
1281   TRACE("(%p, %s).\n",arr,debugstr_guid(riid));
1282
1283   if (!arr || !(arr->fFeatures & FADF_HAVEIID))
1284     return E_INVALIDARG;
1285   memcpy(xiid, riid, sizeof(GUID));
1286   return S_OK;
1287 }
1288
1289 /************************************************************************
1290  *              SafeArrayGetIID (OLEAUT32.67)
1291  */
1292 HRESULT WINAPI SafeArrayGetIID(SAFEARRAY *arr, IID *riid) {
1293   IID *xiid = ((IID*)arr)-1;
1294   TRACE("(%p, %s).\n",arr,debugstr_guid(riid));
1295
1296   if (!arr || !(arr->fFeatures & FADF_HAVEIID))
1297     return E_INVALIDARG;
1298   memcpy(riid, xiid, sizeof(GUID));
1299   return S_OK;
1300 }
1301
1302 /************************************************************************
1303  *              SafeArraySetRecordInfo (OLEAUT32.44)
1304  */
1305 HRESULT WINAPI SafeArraySetRecordInfo(SAFEARRAY *arr, IRecordInfo *iface) {
1306   LPRECORDINFO oldiface;
1307
1308   if (!arr || !(arr->fFeatures & FADF_RECORD))
1309     return E_INVALIDARG;
1310   oldiface = ((IRecordInfo**)arr)[-1];
1311   if (oldiface)
1312     IRecordInfo_Release(oldiface);
1313   ((IRecordInfo**)arr)[-1] = iface;
1314   if (iface)
1315     IRecordInfo_AddRef(iface);
1316   return S_OK;
1317 }
1318
1319 /************************************************************************
1320  *              SafeArrayGetRecordInfo (OLEAUT32.45)
1321  */
1322 HRESULT WINAPI SafeArrayGetRecordInfo(SAFEARRAY *arr, IRecordInfo** iface) {
1323   if (!arr || !(arr->fFeatures & FADF_RECORD))
1324     return E_INVALIDARG;
1325   *iface = ((IRecordInfo**)arr)[-1];
1326   if (*iface)
1327     IRecordInfo_AddRef(*iface);
1328   return S_OK;
1329 }