Fixed the Win16/Win32 structure size/alignment differences of
[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
10 #include <stdio.h>
11 #include <string.h>
12 #include "windef.h"
13 #include "winerror.h"
14 #include "winbase.h"
15 #include "oleauto.h"
16 #include "wine/obj_base.h"
17 #include "debugtools.h"
18
19 DEFAULT_DEBUG_CHANNEL(ole)
20
21 /* Localy used methods */
22 static INT  
23 endOfDim(LONG *coor, SAFEARRAYBOUND *mat, LONG dim, LONG realDim);
24
25 static ULONG   
26 calcDisplacement(LONG *coor, SAFEARRAYBOUND *mat, LONG dim);
27
28 static BOOL  
29 isPointer(USHORT feature);
30
31 static INT   
32 getFeatures(VARTYPE vt);
33
34 static BOOL  
35 validCoordinate(LONG *coor, SAFEARRAY *psa);
36
37 static BOOL  
38 resizeSafeArray(SAFEARRAY *psa, LONG lDelta);
39
40 static BOOL  
41 validArg(SAFEARRAY *psa);
42
43 static ULONG   
44 getArraySize(SAFEARRAY *psa);
45
46 static HRESULT 
47 duplicateData(SAFEARRAY *psa, SAFEARRAY **ppsaOut);
48
49 /* Association between VARTYPE and their size.
50    A size of zero is defined for the unsupported types.  */
51
52 #define VARTYPE_NOT_SUPPORTED 0
53 const static ULONG VARTYPE_SIZE[] =
54 {
55   /* this is taken from wtypes.h.  Only [S]es are supported by the SafeArray */
56 VARTYPE_NOT_SUPPORTED,  /* VT_EMPTY    [V]   [P]    nothing                     */
57 VARTYPE_NOT_SUPPORTED,  /* VT_NULL     [V]   [P]    SQL style Nul       */
58 2,                          /* VT_I2       [V][T][P][S] 2 byte signed int */
59 4,                          /* VT_I4       [V][T][P][S] 4 byte signed int */
60 4,                          /* VT_R4       [V][T][P][S] 4 byte real     */
61 8,                          /* VT_R8       [V][T][P][S] 8 byte real     */
62 8,                      /* VT_CY       [V][T][P][S] currency */
63 8,                          /* VT_DATE     [V][T][P][S] date */
64 4,                          /* VT_BSTR     [V][T][P][S] OLE Automation string*/
65 4,                          /* VT_DISPATCH [V][T][P][S] IDispatch *     */
66 4,                      /* VT_ERROR    [V][T]   [S] SCODE       */
67 4,                          /* VT_BOOL     [V][T][P][S] True=-1, False=0*/
68 24,                     /* VT_VARIANT  [V][T][P][S] VARIANT *   */
69 4,                          /* VT_UNKNOWN  [V][T]   [S] IUnknown * */
70 16,                         /* VT_DECIMAL  [V][T]   [S] 16 byte fixed point     */
71 VARTYPE_NOT_SUPPORTED,                         /* no VARTYPE here..... */
72 VARTYPE_NOT_SUPPORTED,  /* VT_I1          [T]       signed char */
73 1,                          /* VT_UI1      [V][T][P][S] unsigned char                   */
74 VARTYPE_NOT_SUPPORTED,  /* VT_UI2         [T][P]    unsigned short      */
75 VARTYPE_NOT_SUPPORTED,  /* VT_UI4         [T][P]    unsigned short      */
76 VARTYPE_NOT_SUPPORTED,  /* VT_I8          [T][P]    signed 64-bit int                   */
77 VARTYPE_NOT_SUPPORTED,  /* VT_UI8         [T][P]    unsigned 64-bit int         */
78 VARTYPE_NOT_SUPPORTED,  /* VT_INT         [T]       signed machine int          */
79 VARTYPE_NOT_SUPPORTED,  /* VT_UINT        [T]       unsigned machine int        */
80 VARTYPE_NOT_SUPPORTED,  /* VT_VOID        [T]       C style void                        */
81 VARTYPE_NOT_SUPPORTED,  /* VT_HRESULT     [T]       Standard return type        */
82 VARTYPE_NOT_SUPPORTED,  /* VT_PTR         [T]       pointer type                        */
83 VARTYPE_NOT_SUPPORTED,  /* VT_SAFEARRAY   [T]       (use VT_ARRAY in VARIANT)*/
84 VARTYPE_NOT_SUPPORTED,  /* VT_CARRAY      [T]       C style array                       */
85 VARTYPE_NOT_SUPPORTED,  /* VT_USERDEFINED [T]       user defined type                   */
86 VARTYPE_NOT_SUPPORTED,  /* VT_LPSTR       [T][P]    null terminated string      */
87 VARTYPE_NOT_SUPPORTED,  /* VT_LPWSTR      [T][P]    wide null term string               */
88 VARTYPE_NOT_SUPPORTED,  /* VT_FILETIME       [P]    FILETIME                    */
89 VARTYPE_NOT_SUPPORTED,  /* VT_BLOB           [P]    Length prefixed bytes */
90 VARTYPE_NOT_SUPPORTED,  /* VT_STREAM         [P]    Name of stream follows              */
91 VARTYPE_NOT_SUPPORTED,  /* VT_STORAGE        [P]    Name of storage follows     */
92 VARTYPE_NOT_SUPPORTED,  /* VT_STREAMED_OBJECT[P]    Stream contains an object*/
93 VARTYPE_NOT_SUPPORTED,  /* VT_STORED_OBJECT  [P]    Storage contains object*/
94 VARTYPE_NOT_SUPPORTED,  /* VT_BLOB_OBJECT    [P]    Blob contains an object*/
95 VARTYPE_NOT_SUPPORTED,  /* VT_CF             [P]    Clipboard format                    */
96 VARTYPE_NOT_SUPPORTED,  /* VT_CLSID          [P]    A Class ID                  */
97 VARTYPE_NOT_SUPPORTED,  /* VT_VECTOR         [P]    simple counted array                */
98 VARTYPE_NOT_SUPPORTED,  /* VT_ARRAY    [V]          SAFEARRAY*                  */
99 VARTYPE_NOT_SUPPORTED   /* VT_BYREF    [V]          void* for local use */
100 };
101
102 const static int LAST_VARTYPE = sizeof(VARTYPE_SIZE)/sizeof(ULONG);
103
104
105 /*************************************************************************
106  *              SafeArrayAllocDescriptor
107  * Allocate the appropriate amount of memory for the SafeArray descriptor
108  */
109 HRESULT WINAPI SafeArrayAllocDescriptor( 
110   UINT    cDims, 
111   SAFEARRAY **ppsaOut) 
112 {
113   SAFEARRAYBOUND *sab;
114   LONG allocSize = 0;
115
116   /* SAFEARRAY + SAFEARRAYBOUND * (cDims -1) ( -1 because there is already one
117                                              ( in SAFEARRAY struct */
118   allocSize = sizeof(**ppsaOut) + (sizeof(*sab) * (cDims-1));
119
120   /* Allocate memory for SAFEARRAY struc */
121   if(( (*ppsaOut)=HeapAlloc( 
122         GetProcessHeap(), HEAP_ZERO_MEMORY, allocSize)) == NULL){
123     return(E_UNEXPECTED);
124   }
125   TRACE("SafeArray: %lu bytes allocated for descriptor.\n", allocSize);
126
127   return(S_OK);
128 }
129
130 /*************************************************************************
131  *              SafeArrayAllocData
132  * Allocate the appropriate amount of data for the SafeArray data
133  */
134 HRESULT WINAPI SafeArrayAllocData(
135   SAFEARRAY *psa) 
136 {
137   ULONG  ulWholeArraySize;   /* to store the size of the whole thing */
138
139   if(! validArg(psa)) 
140     return E_INVALIDARG;
141
142   ulWholeArraySize = getArraySize(psa);
143
144   /* Allocate memory for the data itself */
145   if((psa->pvData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
146         psa->cbElements*ulWholeArraySize)) == NULL)
147     return(E_UNEXPECTED);
148
149   TRACE("SafeArray: %lu bytes allocated for data at %p (%lu objects).\n", 
150     psa->cbElements*ulWholeArraySize, psa->pvData, ulWholeArraySize);
151
152   return(S_OK);
153 }
154
155 /*************************************************************************
156  *              SafeArrayCreate
157  * Create a SafeArray object by encapsulating AllocDescriptor and AllocData 
158  */
159 SAFEARRAY* WINAPI SafeArrayCreate(
160   VARTYPE        vt, 
161   UINT         cDims, 
162   SAFEARRAYBOUND *rgsabound)
163 {
164   SAFEARRAY *psa;
165   HRESULT   hRes;
166   USHORT    cDim;
167
168   /* Validate supported VARTYPE */
169   if ( (vt >= LAST_VARTYPE) ||
170        ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
171     return NULL;
172
173   /* Allocate memory for the array descriptor */
174   if( FAILED( hRes = SafeArrayAllocDescriptor(cDims, &psa)))
175     return NULL;
176
177   /* setup data members... */ 
178   psa->cDims     = cDims;
179   psa->fFeatures = getFeatures(vt);
180   psa->cLocks    = 0;
181   psa->pvData    = NULL;
182   psa->cbElements= VARTYPE_SIZE[vt];
183
184   /* Invert the bounds ... */
185   for(cDim=0; cDim < psa->cDims; cDim++) {
186     psa->rgsabound[cDim].cElements = rgsabound[psa->cDims-cDim-1].cElements;
187     psa->rgsabound[cDim].lLbound   = rgsabound[psa->cDims-cDim-1].lLbound;
188   }
189
190   /* allocate memory for the data... */ 
191   if( FAILED( hRes = SafeArrayAllocData(psa))) {
192     SafeArrayDestroyDescriptor(psa); 
193     ERR("() : Failed to allocate the Safe Array data\n");
194     return NULL;
195   }
196
197   return(psa); 
198 }
199
200 /*************************************************************************
201  *              SafeArrayDestroyDescriptor
202  * Frees the memory associated with the descriptor.
203  */
204 HRESULT WINAPI SafeArrayDestroyDescriptor(
205   SAFEARRAY *psa)
206 {
207   /* Check for lockness before to free... */
208   if(psa->cLocks > 0) 
209     return DISP_E_ARRAYISLOCKED;
210
211   /* The array is unlocked, then, deallocate memory */
212   if(HeapFree( GetProcessHeap(), 0, psa) == FALSE) 
213     return E_UNEXPECTED;
214   
215   return(S_OK);
216 }
217
218
219 /*************************************************************************
220  *              SafeArrayLock
221  * Increment the lock counter
222  *
223  * Doc says (MSDN Library ) that psa->pvData should be made available (!= NULL)
224  * only when psa->cLocks is > 0... I don't get it since pvData is allocated 
225  * before the array is locked, therefore  
226  */
227 HRESULT WINAPI SafeArrayLock(
228   SAFEARRAY *psa)
229 {
230   if(! validArg(psa))     
231     return E_INVALIDARG;
232
233   psa->cLocks++;
234
235   return(S_OK);
236 }
237
238 /*************************************************************************
239  *              SafeArrayUnlock
240  * Decrement the lock counter
241  */
242 HRESULT WINAPI SafeArrayUnlock(
243   SAFEARRAY *psa)
244 {
245   if(! validArg(psa)) 
246     return E_INVALIDARG;
247
248   if (psa->cLocks > 0) 
249     psa->cLocks--;
250
251   return(S_OK);
252 }
253
254
255 /*************************************************************************
256  *              SafeArrayPutElement
257  * Set the data at the given coordinate
258  */
259 HRESULT WINAPI SafeArrayPutElement(
260   SAFEARRAY *psa, 
261   LONG      *rgIndices, 
262   void      *pv)
263 {
264   ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to 
265                                          the desired one... */
266   PVOID elementStorageAddress = NULL; /* Adress to store the data */
267   BSTR  pbstrReAllocStr     = NULL; /* BSTR reallocated */
268
269   /* Validate the index given */
270   if(! validCoordinate(rgIndices, psa)) 
271     return DISP_E_BADINDEX;
272   if(! validArg(psa))
273     return E_INVALIDARG;
274
275   if( SafeArrayLock(psa) == S_OK) {
276
277     /* Figure out the number of items to skip */
278     stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
279   
280     /* Figure out the number of byte to skip ... */
281     elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
282   
283     if(isPointer(psa->fFeatures)) { /* increment ref count for this pointer */
284
285       *((VOID**)elementStorageAddress) = *(VOID**)pv; 
286       IUnknown_AddRef( *(IUnknown**)pv);
287
288     } else { 
289
290       if(psa->fFeatures == FADF_BSTR) { /* Create a new object */
291
292         if((pbstrReAllocStr = SysAllocString( (OLECHAR*)pv )) == NULL) {
293           SafeArrayUnlock(psa);  
294           return E_OUTOFMEMORY;
295         } else 
296           *((BSTR*)elementStorageAddress) = pbstrReAllocStr;
297
298       } else /* dupplicate the memory */
299         memcpy(elementStorageAddress, pv, SafeArrayGetElemsize(psa) );
300     }
301
302   } else {
303     ERR("SafeArray: Cannot lock array....\n");
304     return E_UNEXPECTED; /* UNDOC error condition */
305   }
306
307   TRACE("SafeArray: item put at adress %p.\n",elementStorageAddress);
308   return SafeArrayUnlock(psa);  
309 }
310
311
312 /*************************************************************************
313  *              SafeArrayGetElement
314  * Return the data element corresponding the the given coordinate
315  */
316 HRESULT WINAPI SafeArrayGetElement(
317   SAFEARRAY *psa, 
318   LONG      *rgIndices, 
319   void      *pv)
320 {
321   ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to 
322                                          the desired one... */
323   PVOID elementStorageAddress = NULL; /* Adress to store the data */
324   BSTR  pbstrReturnedStr    = NULL; /* BSTR reallocated */
325
326   if(! validArg(psa)) 
327     return E_INVALIDARG;
328   
329   if(! validCoordinate(rgIndices, psa)) /* Validate the index given */
330     return(DISP_E_BADINDEX);
331
332   if( SafeArrayLock(psa) == S_OK) {
333
334     /* Figure out the number of items to skip */
335     stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
336   
337     /* Figure out the number of byte to skip ... */
338     elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
339   
340     if( psa->fFeatures == FADF_BSTR) {           /* reallocate the obj */
341       if( (pbstrReturnedStr = 
342           SysAllocString( *(OLECHAR**)elementStorageAddress )) == NULL) {
343         SafeArrayUnlock(psa);
344         return E_OUTOFMEMORY;
345       } else 
346         *((BSTR*)pv) = pbstrReturnedStr; 
347         
348     } else if( isPointer(psa->fFeatures) )       /* simply copy the pointer */
349        pv = *((PVOID*)elementStorageAddress); 
350     else                                         /* copy the bytes */
351       memcpy(pv, elementStorageAddress, SafeArrayGetElemsize(psa) );
352
353   } else {
354     ERR("SafeArray: Cannot lock array....\n");
355     return E_UNEXPECTED; /* UNDOC error condition */
356   }
357
358   return( SafeArrayUnlock(psa) );  
359 }
360
361 /*************************************************************************
362  *              SafeArrayGetUBound
363  * return the UP bound for a given array dimension
364  */
365 HRESULT WINAPI SafeArrayGetUBound(
366   SAFEARRAY *psa, 
367   UINT    nDim,
368   LONG      *plUbound)
369 {
370   if(! validArg(psa))   
371     return E_INVALIDARG;
372
373   if(nDim > psa->cDims) 
374     return DISP_E_BADINDEX;
375
376   *plUbound = psa->rgsabound[nDim-1].lLbound + 
377               psa->rgsabound[nDim-1].cElements - 1;
378
379   return S_OK;
380 }
381
382 /*************************************************************************
383  *              SafeArrayGetLBound
384  * Return the LO bound for a given array dimension 
385  */
386 HRESULT WINAPI SafeArrayGetLBound(
387   SAFEARRAY *psa,
388   UINT    nDim, 
389   LONG      *plLbound)
390 {
391   if(! validArg(psa))   
392     return E_INVALIDARG;
393
394   if(nDim > psa->cDims) 
395     return DISP_E_BADINDEX;
396
397   *plLbound = psa->rgsabound[nDim-1].lLbound;
398   return S_OK;
399 }
400
401 /*************************************************************************
402  *              SafeArrayGetDim
403  * returns the number of dimension in the array
404  */
405 UINT WINAPI SafeArrayGetDim(
406   SAFEARRAY * psa)
407
408   /*
409    * A quick test in Windows shows that the behavior here for an invalid
410    * pointer is to return 0.
411    */
412   if(! validArg(psa)) 
413     return 0;
414
415   return psa->cDims;
416 }
417
418 /*************************************************************************
419  *              SafeArrayGetElemsize
420  * Return the size of the element in the array
421  */
422 UINT WINAPI SafeArrayGetElemsize(
423   SAFEARRAY * psa)
424
425   /*
426    * A quick test in Windows shows that the behavior here for an invalid
427    * pointer is to return 0.
428    */
429   if(! validArg(psa)) 
430     return 0;
431
432   return psa->cbElements;
433 }
434
435 /*************************************************************************
436  *              SafeArrayAccessData
437  * increment the access count and return the data 
438  */
439 HRESULT WINAPI SafeArrayAccessData(
440   SAFEARRAY *psa, 
441   void      **ppvData)
442
443   HRESULT hRes;
444
445   if(! validArg(psa)) 
446     return E_INVALIDARG;
447
448   hRes = SafeArrayLock(psa);
449
450   switch (hRes) {
451     case S_OK: 
452       (*ppvData) = psa->pvData;
453       break;
454     case E_INVALIDARG:
455       (*ppvData) = NULL;
456       return E_INVALIDARG;
457   }
458   
459   return S_OK;
460 }
461
462
463 /*************************************************************************
464  *              SafeArrayUnaccessData
465  * Decrement the access count
466  */
467 HRESULT WINAPI SafeArrayUnaccessData(
468   SAFEARRAY * psa)
469
470   if(! validArg(psa)) 
471     return E_INVALIDARG;
472
473   return(SafeArrayUnlock(psa));
474 }
475
476 /************************************************************************ 
477  *              SafeArrayPtrOfIndex
478  * Return a pointer to the element at rgIndices
479  */
480 HRESULT WINAPI SafeArrayPtrOfIndex(
481   SAFEARRAY *psa, 
482   LONG      *rgIndices, 
483   void      **ppvData)
484
485   ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to 
486                                          the desired one... */
487
488   if(! validArg(psa))                   
489     return E_INVALIDARG;
490
491   if(! validCoordinate(rgIndices, psa)) 
492     return DISP_E_BADINDEX;
493
494   /* Figure out the number of items to skip */
495   stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
496   
497   *ppvData = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
498
499   return S_OK;
500 }
501
502 /************************************************************************ 
503  *              SafeArrayDestroyData
504  * Frees the memory data bloc
505  */
506 HRESULT WINAPI SafeArrayDestroyData(
507   SAFEARRAY *psa)
508
509   HRESULT  hRes;
510   ULONG    ulWholeArraySize; /* count spot in array  */
511   ULONG    ulDataIter;       /* to iterate the data space */
512   IUnknown *punk;
513   BSTR   bstr;
514
515   if(! validArg(psa)) 
516     return E_INVALIDARG;
517
518   if(psa->cLocks > 0) 
519     return DISP_E_ARRAYISLOCKED;
520
521   ulWholeArraySize = getArraySize(psa);
522
523   if(isPointer(psa->fFeatures)) {           /* release the pointers */
524
525     for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
526       punk = *(IUnknown**)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));        
527
528       if( punk != NULL) 
529         IUnknown_Release(punk);
530     }
531
532   } else if(psa->fFeatures & FADF_BSTR) {  /* deallocate the obj */
533
534     for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
535       bstr = *(BSTR*)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));
536
537       if( bstr != NULL) 
538         SysFreeString( bstr );
539     }
540   }
541       
542   /* check if this array is a Vector, in which case do not free the data 
543      block since it has been allocated by AllocDescriptor and therefore
544      deserve to be freed by DestroyDescriptor */
545   if(!(psa->fFeatures & FADF_CREATEVECTOR)) { /* Set when we do CreateVector */
546
547     /* free the whole chunk */
548     if((hRes = HeapFree( GetProcessHeap(), 0, psa->pvData)) == 0) /*falied*/
549       return E_UNEXPECTED; /* UNDOC error condition */
550
551     psa->pvData = NULL;
552   }
553   
554   return S_OK;
555 }
556
557 /************************************************************************ 
558  *              SafeArrayCopyData
559  * Copy the psaSource's data block into psaTarget if dimension and size
560  * permits it.
561  */
562 HRESULT WINAPI SafeArrayCopyData(
563   SAFEARRAY *psaSource,
564   SAFEARRAY **psaTarget)
565
566   USHORT   cDimCount;        /* looper */
567   LONG     lDelta;           /* looper */
568   IUnknown *punk;   
569   ULONG    ulWholeArraySize; /* Number of item in SA */
570   BSTR   bstr;
571
572   if(! (validArg(psaSource) && validArg(*psaTarget)) ) 
573     return E_INVALIDARG;
574
575   if(SafeArrayGetDim(psaSource) != SafeArrayGetDim(*psaTarget))
576     return E_INVALIDARG;
577
578   ulWholeArraySize = getArraySize(psaSource); 
579
580   /* The two arrays boundaries must be of same lenght */
581   for(cDimCount=0;cDimCount < psaSource->cDims; cDimCount++)
582     if( psaSource->rgsabound[cDimCount].cElements != 
583       (*psaTarget)->rgsabound[cDimCount].cElements)
584       return E_INVALIDARG;
585
586   if( isPointer((*psaTarget)->fFeatures) ) {         /* the target contains ptr 
587                                                         that must be released */
588     for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
589       punk = *(IUnknown**)
590         ((char *) (*psaTarget)->pvData + (lDelta * (*psaTarget)->cbElements));
591
592       if( punk != NULL) 
593         IUnknown_Release(punk);
594     }
595
596   } else if( (*psaTarget)->fFeatures & FADF_BSTR) {  /* the target contain BSTR
597                                                         that must be freed */ 
598     for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
599       bstr = 
600         *(BSTR*)((char *) (*psaTarget)->pvData + (lDelta * (*psaTarget)->cbElements));
601
602       if( bstr != NULL) 
603         SysFreeString( bstr );
604     }
605   }
606
607   return duplicateData(psaSource, psaTarget);
608 }
609
610 /************************************************************************ 
611  *              SafeArrayDestroy
612  * Deallocates all memory reserved for the SafeArray
613  */
614 HRESULT WINAPI SafeArrayDestroy(
615   SAFEARRAY * psa)
616
617   HRESULT hRes;
618
619   if(! validArg(psa)) 
620     return E_INVALIDARG;
621
622   if(psa->cLocks > 0) 
623     return DISP_E_ARRAYISLOCKED;
624
625   if((hRes = SafeArrayDestroyData( psa )) == S_OK)
626     if((hRes = SafeArrayDestroyDescriptor( psa )) == S_OK)
627       return S_OK;
628
629   return E_UNEXPECTED; /* UNDOC error condition */
630 }
631
632 /************************************************************************ 
633  *              SafeArrayCopy
634  * Make a dupplicate of a SafeArray
635  */
636 HRESULT WINAPI SafeArrayCopy(
637   SAFEARRAY *psa, 
638   SAFEARRAY **ppsaOut)
639
640   HRESULT hRes;
641   DWORD   dAllocSize;
642   ULONG   ulWholeArraySize; /* size of the thing */
643
644   if(! validArg(psa)) 
645     return E_INVALIDARG;
646
647   if((hRes=SafeArrayAllocDescriptor(psa->cDims, ppsaOut)) == S_OK){
648
649     /* Duplicate the SAFEARRAY struc */
650     memcpy(*ppsaOut, psa, 
651             sizeof(*psa)+(sizeof(*(psa->rgsabound))*(psa->cDims-1)));
652
653     (*ppsaOut)->pvData = NULL; /* do not point to the same data area */
654
655     /* make sure the new safe array doesn't have the FADF_CREATEVECTOR flag,
656        because the data has not been allocated with the descriptor. */
657     (*ppsaOut)->fFeatures &= ~FADF_CREATEVECTOR;  
658  
659     /* Get the allocated memory size for source and allocate it for target */ 
660     ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
661     dAllocSize = ulWholeArraySize*psa->cbElements;
662
663     (*ppsaOut)->pvData = 
664       HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dAllocSize);
665     if( (*ppsaOut)->pvData != NULL) {   /* HeapAlloc succeed */
666
667       if( (hRes=duplicateData(psa, ppsaOut)) != S_OK) { /* E_OUTOFMEMORY */
668         HeapFree(GetProcessHeap(), 0, (*ppsaOut)->pvData);
669         (*ppsaOut)->pvData = NULL;
670         SafeArrayDestroyDescriptor(*ppsaOut);
671         return hRes;
672       }
673         
674     } else { /* failed to allocate or dupplicate... */
675       SafeArrayDestroyDescriptor(*ppsaOut);
676       return E_UNEXPECTED; /* UNDOC error condition */
677     }
678   } else { /* failed to allocate mem for descriptor */
679     return E_OUTOFMEMORY; /* UNDOC error condiftion */
680   }
681
682   return S_OK;
683 }
684
685 /************************************************************************ 
686  *              SafeArrayCreateVector
687  * Creates a one dimension safearray where the data is next to the 
688  * SAFEARRAY structure.
689  */
690 SAFEARRAY* WINAPI SafeArrayCreateVector(
691   VARTYPE vt, 
692   LONG    lLbound, 
693   ULONG   cElements) 
694
695   SAFEARRAY *psa;
696
697   /* Validate supported VARTYPE */
698   if ( (vt >= LAST_VARTYPE) ||
699        ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
700     return NULL;
701
702   /* Allocate memory for the array descriptor and data contiguously  */
703   if( FAILED( psa = HeapAlloc( GetProcessHeap(), 
704                       HEAP_ZERO_MEMORY, 
705                       (sizeof(*psa) + (VARTYPE_SIZE[vt] * cElements))))) {
706     return NULL;
707   }
708                                                                                 
709   /* setup data members... */ 
710   psa->cDims      = 1; /* always and forever */
711   psa->fFeatures  = getFeatures(vt) | FADF_CREATEVECTOR;  /* undocumented flag used by Microsoft */
712   psa->cLocks     = 0;
713   psa->pvData     = (BYTE*)psa + sizeof(*psa);
714   psa->cbElements = VARTYPE_SIZE[vt];
715
716   psa->rgsabound[0].cElements = cElements;
717   psa->rgsabound[0].lLbound   = lLbound;
718
719   return(psa);                            
720
721
722 /************************************************************************ 
723  *              SafeArrayRedim
724  * Changes the caracteristics of the last dimension of the SafeArray
725  */
726 HRESULT WINAPI SafeArrayRedim(
727   SAFEARRAY      *psa, 
728   SAFEARRAYBOUND *psaboundNew)
729
730   LONG   lDelta;  /* hold difference in size */
731   USHORT cDims=1; /* dims counter */
732
733   if( !validArg(psa) )                    
734     return E_INVALIDARG;
735
736   if( psa->cLocks > 0 )                    
737     return DISP_E_ARRAYISLOCKED;
738
739   if( psa->fFeatures & FADF_FIXEDSIZE )    
740     return E_INVALIDARG;
741
742   if( SafeArrayLock(psa)==E_UNEXPECTED ) 
743     return E_UNEXPECTED;/* UNDOC error condition */
744
745   /* find the delta in number of array spot to apply to the new array */
746   lDelta = psaboundNew->cElements - psa->rgsabound[0].cElements;
747   for(; cDims < psa->cDims; cDims++)
748     /* delta in number of spot implied by modifying the last dimension */
749     lDelta *= psa->rgsabound[cDims].cElements;
750
751   if (lDelta == 0) { ;/* same size, maybe a change of lLbound, just set it */
752
753   } else /* need to enlarge (lDelta +) reduce (lDelta -) */
754     if(! resizeSafeArray(psa, lDelta)) 
755       return E_UNEXPECTED; /* UNDOC error condition */
756
757   /* the only modifyable dimension sits in [0] as the dimensions were reversed  
758      at array creation time... */
759   psa->rgsabound[0].cElements = psaboundNew->cElements;
760   psa->rgsabound[0].lLbound   = psaboundNew->lLbound;
761
762   return SafeArrayUnlock(psa);
763 }
764
765 /************************************************************************
766  * NOT WINDOWS API - SafeArray* Utility functions
767  ************************************************************************/
768
769 /************************************************************************ 
770  * Used to validate the SAFEARRAY type of arg
771  */
772 static BOOL validArg(
773   SAFEARRAY *psa) 
774 {
775   SAFEARRAYBOUND *sab;
776   LONG psaSize  = 0;
777   LONG descSize = 0;
778   LONG fullSize = 0;
779
780   /*
781    * Let's check for the null pointer just in case.
782    */
783   if (psa == NULL)
784     return FALSE;
785
786   /* Check whether the size of the chunk make sens... That's the only thing
787      I can think of now... */
788
789   psaSize   = HeapSize(GetProcessHeap(), 0, psa);
790
791   /* size of the descriptor when the SA is not created with CreateVector */
792   descSize = sizeof(*psa) + (sizeof(*sab) * (psa->cDims-1));
793
794   /* size of the descriptor + data when created with CreateVector */
795   fullSize = sizeof(*psa) + (psa->cbElements * psa->rgsabound[0].cElements);
796
797   return((psaSize >= descSize) || (psaSize >= fullSize));
798 }
799
800 /************************************************************************ 
801  * Used to reallocate memory
802  */
803 static BOOL resizeSafeArray(
804   SAFEARRAY *psa, 
805   LONG lDelta)
806 {
807   ULONG    ulWholeArraySize;  /* use as multiplicator */
808   PVOID    pvNewBlock = NULL;  
809   IUnknown *punk;
810   BSTR   bstr;
811
812   ulWholeArraySize = getArraySize(psa);
813
814   if(lDelta < 0) {                    /* array needs to be shorthen  */
815     if( isPointer(psa->fFeatures))    /* ptr that need to be released */
816             for(;lDelta < 0; lDelta++) {
817               punk = *(IUnknown**)
818           ((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
819         
820         if( punk != NULL )
821           IUnknown_Release(punk);
822             }
823
824     else if(psa->fFeatures & FADF_BSTR)  /* BSTR that need to be freed */
825             for(;lDelta < 0; lDelta++) {
826         bstr = *(BSTR*)
827           ((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
828
829         if( bstr != NULL )
830           SysFreeString( bstr );
831       }
832   }
833
834   if (!(psa->fFeatures & FADF_CREATEVECTOR))
835   {
836     /* Ok now, if we are enlarging the array, we *MUST* move the whole block 
837        pointed to by pvData.   If we are shorthening the array, this move is
838        optional but we do it anyway becuase the benefit is that we are 
839        releasing to the system the unused memory */
840
841     if((pvNewBlock = HeapReAlloc(GetProcessHeap(), 0, psa->pvData, 
842        (ulWholeArraySize + lDelta) * psa->cbElements)) == NULL) 
843         return FALSE; /* TODO If we get here it means:
844                          SHRINK situation :  we've deleted the undesired
845                                              data and did not release the memory
846                          GROWING situation:  we've been unable to grow the array
847                       */
848   }
849   else
850   {
851     /* Allocate a new block, because the previous data has been allocated with 
852        the descriptor in SafeArrayCreateVector function. */
853
854     if((pvNewBlock = HeapAlloc(GetProcessHeap(), 0,
855        ulWholeArraySize * psa->cbElements)) == NULL) 
856         return FALSE;
857
858     psa->fFeatures &= ~FADF_CREATEVECTOR;
859   }
860   /* reassign to the new block of data */
861   psa->pvData = pvNewBlock;
862   return TRUE;
863 }
864
865 /************************************************************************ 
866  * Used to set the fFeatures data member of the SAFEARRAY structure. 
867  */
868 static INT getFeatures(
869   VARTYPE vt) 
870 {
871   switch(vt) {
872     case VT_UNKNOWN:   return FADF_UNKNOWN;
873     case VT_DISPATCH:  return FADF_DISPATCH;
874     case VT_BSTR:      return FADF_BSTR;
875   }
876   return 0;
877 }
878
879 /************************************************************************ 
880  * Used to figure out if the fFeatures data member of the SAFEARRAY 
881  * structure contain any information about the type of data stored... 
882  */
883 static BOOL isPointer(
884   USHORT feature) 
885 {
886   switch(feature) {
887     case FADF_UNKNOWN:  return TRUE; /* those are pointers */
888     case FADF_DISPATCH: return TRUE;
889   }
890   return FALSE;
891 }
892
893 /************************************************************************ 
894  * Used to calculate the displacement when accessing or modifying 
895  * safearray data set.
896  *
897  *  Parameters: - LONG *coor is the desired location in the multidimension
898  *              table.  Ex for a 3 dim table: coor[] = {1,2,3};
899  *              - ULONG *mat is the format of the table.  Ex for a 3 dim
900  *              table mat[] = {4,4,4};
901  *              - USHORT dim is the number of dimension of the SafeArray
902  */
903 static ULONG calcDisplacement(
904   LONG           *coor, 
905   SAFEARRAYBOUND *mat, 
906   LONG           dim) 
907 {
908   ULONG res = 0;
909   LONG  iterDim;
910
911   for(iterDim=0; iterDim<dim; iterDim++) 
912     /* the -mat[dim] bring coor[dim] relative to 0 for calculation */
913     res += ((coor[iterDim]-mat[iterDim].lLbound) * 
914             endOfDim(coor, mat, iterDim+1, dim));
915
916   TRACE("SafeArray: calculated displacement is %lu.\n", res);
917   return(res);
918 }
919
920 /************************************************************************ 
921  * Recursivity agent for calcDisplacement method.  Used within Put and 
922  * Get methods.
923  */
924 static INT endOfDim(
925   LONG           *coor, 
926   SAFEARRAYBOUND *mat, 
927   LONG           dim, 
928   LONG           realDim) 
929 {
930   if(dim==realDim) 
931     return 1;
932   else 
933     return (endOfDim(coor, mat, dim+1, realDim) * mat[dim].cElements);
934 }
935
936
937 /************************************************************************ 
938  * Method used to validate the coordinate received in Put and Get 
939  * methods.
940  */
941 static BOOL validCoordinate(
942   LONG      *coor, 
943   SAFEARRAY *psa) 
944 {
945   INT   iter=0;
946   LONG    lUBound;
947   LONG    lLBound;
948   HRESULT hRes;
949
950   for(; iter<psa->cDims; iter++) {
951     if((hRes = SafeArrayGetLBound(psa, iter, &lLBound)) != S_OK)
952       return FALSE;
953     if((hRes = SafeArrayGetUBound(psa, iter, &lUBound)) != S_OK)
954       return FALSE;
955  
956     if(lLBound == lUBound) 
957       return FALSE; 
958    
959     if((coor[iter] >= lLBound) && (coor[iter] <= lUBound))
960       return TRUE;
961     else
962       return FALSE;
963   }
964   return FALSE;
965 }  
966
967 /************************************************************************ 
968  * Method used to calculate the number of cells of the SA
969  */
970 static ULONG getArraySize(
971   SAFEARRAY *psa) 
972 {
973   USHORT cCount; 
974   ULONG  ulWholeArraySize = 1;
975
976   for(cCount=0; cCount < psa->cDims; cCount++) /* foreach dimensions... */
977     ulWholeArraySize *= psa->rgsabound[cCount].cElements;
978
979   return ulWholeArraySize;  
980 }
981
982
983 /************************************************************************ 
984  * Method used to handle data space dupplication for Copy32 and CopyData32
985  */
986 static HRESULT duplicateData(
987   SAFEARRAY *psa, 
988   SAFEARRAY **ppsaOut) 
989 {
990   ULONG    ulWholeArraySize; /* size of the thing */
991   LONG     lDelta;
992   IUnknown *punk;
993   BSTR   pbstrReAllocStr = NULL; /* BSTR reallocated */
994
995   ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
996   
997   SafeArrayLock(*ppsaOut);
998
999   if( isPointer(psa->fFeatures) ) {  /* If datatype is object increment 
1000                                         object's reference count */
1001
1002     for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1003       punk = *(IUnknown**)((char *) psa->pvData+(lDelta * psa->cbElements));
1004
1005       if( punk != NULL)
1006         IUnknown_AddRef(punk);
1007     }
1008
1009     /* Copy the source array data into target array */
1010     memcpy((*ppsaOut)->pvData, psa->pvData, 
1011       ulWholeArraySize*psa->cbElements);
1012
1013   } else if( psa->fFeatures & FADF_BSTR ) { /* if datatype is BSTR allocate 
1014                                                the BSTR in the new array */
1015
1016     for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1017       if(( pbstrReAllocStr = SysAllocString(
1018             *(BSTR*)((char *) psa->pvData+(lDelta * psa->cbElements)))) == NULL) {
1019
1020         SafeArrayUnlock(*ppsaOut);
1021         return E_OUTOFMEMORY;
1022       }
1023
1024       *((BSTR*)((char *) (*ppsaOut)->pvData+(lDelta * psa->cbElements))) = 
1025         pbstrReAllocStr;
1026     }
1027
1028   } else { /* Simply copy the source array data into target array */
1029
1030     memcpy((*ppsaOut)->pvData, psa->pvData, 
1031       ulWholeArraySize*psa->cbElements);
1032   }
1033
1034   SafeArrayUnlock(*ppsaOut);
1035
1036   return S_OK;
1037 }
1038
1039
1040 /************************************************************************ 
1041  *              SafeArrayGetVarType
1042  * Returns the VARTYPE stored in the given safearray
1043  */
1044 HRESULT WINAPI SafeArrayGetVarType(
1045   SAFEARRAY* psa,
1046   VARTYPE*   pvt)
1047 {
1048   HRESULT hr = E_INVALIDARG;
1049   VARTYPE vt = VT_EMPTY;
1050
1051   /* const short VARTYPE_OFFSET = -4; */
1052
1053   if (psa->fFeatures & FADF_HAVEVARTYPE)
1054   {
1055     /* VT tag @ negative offset 4 in the array descriptor */
1056     FIXME("Returning VT_BSTR instead of VT_...");
1057     vt = VT_BSTR;
1058   }
1059   else if (psa->fFeatures & FADF_RECORD)
1060   {
1061     vt = VT_RECORD;
1062   }
1063   else if (psa->fFeatures & FADF_DISPATCH)
1064   {
1065     vt = VT_DISPATCH;
1066   }
1067   else if (psa->fFeatures & FADF_UNKNOWN)
1068   {
1069     vt = VT_UNKNOWN;
1070   }
1071
1072   if (vt != VT_EMPTY)
1073   {
1074     *pvt = vt;
1075     hr = S_OK;
1076   }
1077   
1078   TRACE("HRESULT = %08lx", hr);
1079   return hr;
1080 }