- Implemented setlocale parsing and LC_TYPE behavior.
[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 static const 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 static const 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 makes sense... That's the only thing
787      I can think of now... */
788
789   psaSize = HeapSize(GetProcessHeap(), 0, psa);
790   if (psaSize == -1)
791     /* uh, foreign heap. Better don't mess with it ! */
792     return TRUE;
793
794   /* size of the descriptor when the SA is not created with CreateVector */
795   descSize = sizeof(*psa) + (sizeof(*sab) * (psa->cDims-1));
796
797   /* size of the descriptor + data when created with CreateVector */
798   fullSize = sizeof(*psa) + (psa->cbElements * psa->rgsabound[0].cElements);
799
800   return((psaSize >= descSize) || (psaSize >= fullSize));
801 }
802
803 /************************************************************************ 
804  * Used to reallocate memory
805  */
806 static BOOL resizeSafeArray(
807   SAFEARRAY *psa, 
808   LONG lDelta)
809 {
810   ULONG    ulWholeArraySize;  /* use as multiplicator */
811   PVOID    pvNewBlock = NULL;  
812   IUnknown *punk;
813   BSTR   bstr;
814
815   ulWholeArraySize = getArraySize(psa);
816
817   if(lDelta < 0) {                    /* array needs to be shorthen  */
818     if( isPointer(psa->fFeatures))    /* ptr that need to be released */
819             for(;lDelta < 0; lDelta++) {
820               punk = *(IUnknown**)
821           ((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
822         
823         if( punk != NULL )
824           IUnknown_Release(punk);
825             }
826
827     else if(psa->fFeatures & FADF_BSTR)  /* BSTR that need to be freed */
828             for(;lDelta < 0; lDelta++) {
829         bstr = *(BSTR*)
830           ((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
831
832         if( bstr != NULL )
833           SysFreeString( bstr );
834       }
835   }
836
837   if (!(psa->fFeatures & FADF_CREATEVECTOR))
838   {
839     /* Ok now, if we are enlarging the array, we *MUST* move the whole block 
840        pointed to by pvData.   If we are shorthening the array, this move is
841        optional but we do it anyway becuase the benefit is that we are 
842        releasing to the system the unused memory */
843
844     if((pvNewBlock = HeapReAlloc(GetProcessHeap(), 0, psa->pvData, 
845        (ulWholeArraySize + lDelta) * psa->cbElements)) == NULL) 
846         return FALSE; /* TODO If we get here it means:
847                          SHRINK situation :  we've deleted the undesired
848                                              data and did not release the memory
849                          GROWING situation:  we've been unable to grow the array
850                       */
851   }
852   else
853   {
854     /* Allocate a new block, because the previous data has been allocated with 
855        the descriptor in SafeArrayCreateVector function. */
856
857     if((pvNewBlock = HeapAlloc(GetProcessHeap(), 0,
858        ulWholeArraySize * psa->cbElements)) == NULL) 
859         return FALSE;
860
861     psa->fFeatures &= ~FADF_CREATEVECTOR;
862   }
863   /* reassign to the new block of data */
864   psa->pvData = pvNewBlock;
865   return TRUE;
866 }
867
868 /************************************************************************ 
869  * Used to set the fFeatures data member of the SAFEARRAY structure. 
870  */
871 static INT getFeatures(
872   VARTYPE vt) 
873 {
874   switch(vt) {
875     case VT_UNKNOWN:   return FADF_UNKNOWN;
876     case VT_DISPATCH:  return FADF_DISPATCH;
877     case VT_BSTR:      return FADF_BSTR;
878   }
879   return 0;
880 }
881
882 /************************************************************************ 
883  * Used to figure out if the fFeatures data member of the SAFEARRAY 
884  * structure contain any information about the type of data stored... 
885  */
886 static BOOL isPointer(
887   USHORT feature) 
888 {
889   switch(feature) {
890     case FADF_UNKNOWN:  return TRUE; /* those are pointers */
891     case FADF_DISPATCH: return TRUE;
892   }
893   return FALSE;
894 }
895
896 /************************************************************************ 
897  * Used to calculate the displacement when accessing or modifying 
898  * safearray data set.
899  *
900  *  Parameters: - LONG *coor is the desired location in the multidimension
901  *              table.  Ex for a 3 dim table: coor[] = {1,2,3};
902  *              - ULONG *mat is the format of the table.  Ex for a 3 dim
903  *              table mat[] = {4,4,4};
904  *              - USHORT dim is the number of dimension of the SafeArray
905  */
906 static ULONG calcDisplacement(
907   LONG           *coor, 
908   SAFEARRAYBOUND *mat, 
909   LONG           dim) 
910 {
911   ULONG res = 0;
912   LONG  iterDim;
913
914   for(iterDim=0; iterDim<dim; iterDim++) 
915     /* the -mat[dim] bring coor[dim] relative to 0 for calculation */
916     res += ((coor[iterDim]-mat[iterDim].lLbound) * 
917             endOfDim(coor, mat, iterDim+1, dim));
918
919   TRACE("SafeArray: calculated displacement is %lu.\n", res);
920   return(res);
921 }
922
923 /************************************************************************ 
924  * Recursivity agent for calcDisplacement method.  Used within Put and 
925  * Get methods.
926  */
927 static INT endOfDim(
928   LONG           *coor, 
929   SAFEARRAYBOUND *mat, 
930   LONG           dim, 
931   LONG           realDim) 
932 {
933   if(dim==realDim) 
934     return 1;
935   else 
936     return (endOfDim(coor, mat, dim+1, realDim) * mat[dim].cElements);
937 }
938
939
940 /************************************************************************ 
941  * Method used to validate the coordinate received in Put and Get 
942  * methods.
943  */
944 static BOOL validCoordinate(
945   LONG      *coor, 
946   SAFEARRAY *psa) 
947 {
948   INT   iter=0;
949   LONG    lUBound;
950   LONG    lLBound;
951   HRESULT hRes;
952
953   for(; iter<psa->cDims; iter++) {
954     if((hRes = SafeArrayGetLBound(psa, iter, &lLBound)) != S_OK)
955       return FALSE;
956     if((hRes = SafeArrayGetUBound(psa, iter, &lUBound)) != S_OK)
957       return FALSE;
958  
959     if(lLBound == lUBound) 
960       return FALSE; 
961    
962     if((coor[iter] >= lLBound) && (coor[iter] <= lUBound))
963       return TRUE;
964     else
965       return FALSE;
966   }
967   return FALSE;
968 }  
969
970 /************************************************************************ 
971  * Method used to calculate the number of cells of the SA
972  */
973 static ULONG getArraySize(
974   SAFEARRAY *psa) 
975 {
976   USHORT cCount; 
977   ULONG  ulWholeArraySize = 1;
978
979   for(cCount=0; cCount < psa->cDims; cCount++) /* foreach dimensions... */
980     ulWholeArraySize *= psa->rgsabound[cCount].cElements;
981
982   return ulWholeArraySize;  
983 }
984
985
986 /************************************************************************ 
987  * Method used to handle data space dupplication for Copy32 and CopyData32
988  */
989 static HRESULT duplicateData(
990   SAFEARRAY *psa, 
991   SAFEARRAY **ppsaOut) 
992 {
993   ULONG    ulWholeArraySize; /* size of the thing */
994   LONG     lDelta;
995   IUnknown *punk;
996   BSTR   pbstrReAllocStr = NULL; /* BSTR reallocated */
997
998   ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
999   
1000   SafeArrayLock(*ppsaOut);
1001
1002   if( isPointer(psa->fFeatures) ) {  /* If datatype is object increment 
1003                                         object's reference count */
1004
1005     for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1006       punk = *(IUnknown**)((char *) psa->pvData+(lDelta * psa->cbElements));
1007
1008       if( punk != NULL)
1009         IUnknown_AddRef(punk);
1010     }
1011
1012     /* Copy the source array data into target array */
1013     memcpy((*ppsaOut)->pvData, psa->pvData, 
1014       ulWholeArraySize*psa->cbElements);
1015
1016   } else if( psa->fFeatures & FADF_BSTR ) { /* if datatype is BSTR allocate 
1017                                                the BSTR in the new array */
1018
1019     for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1020       if(( pbstrReAllocStr = SysAllocString(
1021             *(BSTR*)((char *) psa->pvData+(lDelta * psa->cbElements)))) == NULL) {
1022
1023         SafeArrayUnlock(*ppsaOut);
1024         return E_OUTOFMEMORY;
1025       }
1026
1027       *((BSTR*)((char *) (*ppsaOut)->pvData+(lDelta * psa->cbElements))) = 
1028         pbstrReAllocStr;
1029     }
1030
1031   } else { /* Simply copy the source array data into target array */
1032
1033     memcpy((*ppsaOut)->pvData, psa->pvData, 
1034       ulWholeArraySize*psa->cbElements);
1035   }
1036
1037   SafeArrayUnlock(*ppsaOut);
1038
1039   return S_OK;
1040 }
1041
1042
1043 /************************************************************************ 
1044  *              SafeArrayGetVarType
1045  * Returns the VARTYPE stored in the given safearray
1046  */
1047 HRESULT WINAPI SafeArrayGetVarType(
1048   SAFEARRAY* psa,
1049   VARTYPE*   pvt)
1050 {
1051   HRESULT hr = E_INVALIDARG;
1052   VARTYPE vt = VT_EMPTY;
1053
1054   /* const short VARTYPE_OFFSET = -4; */
1055
1056   if (psa->fFeatures & FADF_HAVEVARTYPE)
1057   {
1058     /* VT tag @ negative offset 4 in the array descriptor */
1059     FIXME("Returning VT_BSTR instead of VT_...");
1060     vt = VT_BSTR;
1061   }
1062   else if (psa->fFeatures & FADF_RECORD)
1063   {
1064     vt = VT_RECORD;
1065   }
1066   else if (psa->fFeatures & FADF_DISPATCH)
1067   {
1068     vt = VT_DISPATCH;
1069   }
1070   else if (psa->fFeatures & FADF_UNKNOWN)
1071   {
1072     vt = VT_UNKNOWN;
1073   }
1074
1075   if (vt != VT_EMPTY)
1076   {
1077     *pvt = vt;
1078     hr = S_OK;
1079   }
1080   
1081   TRACE("HRESULT = %08lx", hr);
1082   return hr;
1083 }