Use the Unicode string functions from wine/unicode.h instead of the
[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_FIXEDSIZE)) { /* 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
643   if(! validArg(psa)) 
644     return E_INVALIDARG;
645
646   if((hRes=SafeArrayAllocDescriptor(psa->cDims, ppsaOut)) == S_OK){
647
648     /* Duplicate the SAFEARRAY struc */
649     memcpy(*ppsaOut, psa, 
650             sizeof(*psa)+(sizeof(*(psa->rgsabound))*(psa->cDims-1)));
651
652     (*ppsaOut)->pvData = NULL; /* do not point to the same data area */
653   
654     /* Get the allocated memory size for source and allocate it for target */ 
655     dAllocSize = HeapSize(GetProcessHeap(), 0, psa->pvData);
656     (*ppsaOut)->pvData = 
657       HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dAllocSize);
658
659     if( (*ppsaOut)->pvData != NULL) {   /* HeapAlloc succeed */
660
661       if( (hRes=duplicateData(psa, ppsaOut)) != S_OK) { /* E_OUTOFMEMORY */
662         HeapFree(GetProcessHeap(), 0, (*ppsaOut)->pvData);
663         (*ppsaOut)->pvData = NULL;
664         SafeArrayDestroyDescriptor(*ppsaOut);
665         return hRes;
666       }
667         
668     } else { /* failed to allocate or dupplicate... */
669       SafeArrayDestroyDescriptor(*ppsaOut);
670       return E_UNEXPECTED; /* UNDOC error condition */
671     }
672   } else { /* failed to allocate mem for descriptor */
673     return E_OUTOFMEMORY; /* UNDOC error condiftion */
674   }
675
676   return S_OK;
677 }
678
679 /************************************************************************ 
680  *              SafeArrayCreateVector
681  * Creates a one dimension safearray where the data is next to the 
682  * SAFEARRAY structure.
683  */
684 SAFEARRAY* WINAPI SafeArrayCreateVector(
685   VARTYPE vt, 
686   LONG    lLbound, 
687   ULONG   cElements) 
688
689   SAFEARRAY *psa;
690
691   /* Validate supported VARTYPE */
692   if ( (vt >= LAST_VARTYPE) ||
693        ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
694     return NULL;
695
696   /* Allocate memory for the array descriptor and data contiguously  */
697   if( FAILED( psa = HeapAlloc( GetProcessHeap(), 
698                       HEAP_ZERO_MEMORY, 
699                       (sizeof(*psa) + (VARTYPE_SIZE[vt] * cElements))))) {
700     return NULL;
701   }
702
703   /* setup data members... */ 
704   psa->cDims      = 1; /* always and forever */
705   psa->fFeatures  = getFeatures(vt) | FADF_FIXEDSIZE;
706   psa->cLocks     = 0;
707   psa->pvData     = psa+sizeof(*psa);
708   psa->cbElements = VARTYPE_SIZE[vt];
709
710   psa->rgsabound[0].cElements = cElements;
711   psa->rgsabound[0].lLbound   = lLbound;
712   
713   return(psa); 
714
715
716 /************************************************************************ 
717  *              SafeArrayRedim
718  * Changes the caracteristics of the last dimension of the SafeArray
719  */
720 HRESULT WINAPI SafeArrayRedim(
721   SAFEARRAY      *psa, 
722   SAFEARRAYBOUND *psaboundNew)
723
724   LONG   lDelta;  /* hold difference in size */
725   USHORT cDims=1; /* dims counter */
726
727   if( !validArg(psa) )                    
728     return E_INVALIDARG;
729
730   if( psa->cLocks > 0 )                    
731     return DISP_E_ARRAYISLOCKED;
732
733   if( psa->fFeatures & FADF_FIXEDSIZE )    
734     return E_INVALIDARG;
735
736   if( SafeArrayLock(psa)==E_UNEXPECTED ) 
737     return E_UNEXPECTED;/* UNDOC error condition */
738
739   /* find the delta in number of array spot to apply to the new array */
740   lDelta = psaboundNew->cElements - psa->rgsabound[0].cElements;
741   for(; cDims < psa->cDims; cDims++)
742     /* delta in number of spot implied by modifying the last dimension */
743     lDelta *= psa->rgsabound[cDims].cElements;
744
745   if (lDelta == 0) { ;/* same size, maybe a change of lLbound, just set it */
746
747   } else /* need to enlarge (lDelta +) reduce (lDelta -) */
748     if(! resizeSafeArray(psa, lDelta)) 
749       return E_UNEXPECTED; /* UNDOC error condition */
750
751   /* the only modifyable dimension sits in [0] as the dimensions were reversed  
752      at array creation time... */
753   psa->rgsabound[0].cElements = psaboundNew->cElements;
754   psa->rgsabound[0].lLbound   = psaboundNew->lLbound;
755
756   return SafeArrayUnlock(psa);
757 }
758
759 /************************************************************************
760  * NOT WINDOWS API - SafeArray* Utility functions
761  ************************************************************************/
762
763 /************************************************************************ 
764  * Used to validate the SAFEARRAY type of arg
765  */
766 static BOOL validArg(
767   SAFEARRAY *psa) 
768 {
769   SAFEARRAYBOUND *sab;
770   LONG psaSize  = 0;
771   LONG descSize = 0;
772   LONG fullSize = 0;
773
774   /*
775    * Let's check for the null pointer just in case.
776    */
777   if (psa == NULL)
778     return FALSE;
779
780   /* Check whether the size of the chunk make sens... That's the only thing
781      I can think of now... */
782
783   psaSize   = HeapSize(GetProcessHeap(), 0, psa);
784
785   /* size of the descriptor when the SA is not created with CreateVector */
786   descSize = sizeof(*psa) + (sizeof(*sab) * (psa->cDims-1));
787
788   /* size of the descriptor + data when created with CreateVector */
789   fullSize = sizeof(*psa) + (psa->cbElements * psa->rgsabound[0].cElements);
790
791   return((psaSize >= descSize) || (psaSize >= fullSize));
792 }
793
794 /************************************************************************ 
795  * Used to reallocate memory
796  */
797 static BOOL resizeSafeArray(
798   SAFEARRAY *psa, 
799   LONG lDelta)
800 {
801   ULONG    ulWholeArraySize;  /* use as multiplicator */
802   PVOID    pvNewBlock = NULL;  
803   IUnknown *punk;
804   BSTR   bstr;
805
806   ulWholeArraySize = getArraySize(psa);
807
808   if(lDelta < 0) {                    /* array needs to be shorthen  */
809     if( isPointer(psa->fFeatures))    /* ptr that need to be released */
810             for(;lDelta < 0; lDelta++) {
811               punk = *(IUnknown**)
812           ((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
813         
814         if( punk != NULL )
815           IUnknown_Release(punk);
816             }
817
818     else if(psa->fFeatures & FADF_BSTR)  /* BSTR that need to be freed */
819             for(;lDelta < 0; lDelta++) {
820         bstr = *(BSTR*)
821           ((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
822
823         if( bstr != NULL )
824           SysFreeString( bstr );
825       }
826   }
827
828   /* Ok now, if we are enlarging the array, we *MUST* move the whole block 
829      pointed to by pvData.   If we are shorthening the array, this move is
830      optional but we do it anyway becuase the benefit is that we are 
831      releasing to the system the unused memory */
832   
833   if((pvNewBlock = HeapReAlloc(GetProcessHeap(), 0, psa->pvData, 
834      (ulWholeArraySize + lDelta) * psa->cbElements)) == NULL) 
835       return FALSE; /* TODO If we get here it means:
836                        SHRINK situation :  we've deleted the undesired
837                                            data and did not release the memory
838                        GROWING situation:  we've been unable to grow the array
839                     */
840
841   /* reassign to the new block of data */
842   psa->pvData = pvNewBlock;
843   return TRUE;
844 }
845
846 /************************************************************************ 
847  * Used to set the fFeatures data member of the SAFEARRAY structure. 
848  */
849 static INT getFeatures(
850   VARTYPE vt) 
851 {
852   switch(vt) {
853     case VT_UNKNOWN:   return FADF_UNKNOWN;
854     case VT_DISPATCH:  return FADF_DISPATCH;
855     case VT_BSTR:      return FADF_BSTR;
856   }
857   return 0;
858 }
859
860 /************************************************************************ 
861  * Used to figure out if the fFeatures data member of the SAFEARRAY 
862  * structure contain any information about the type of data stored... 
863  */
864 static BOOL isPointer(
865   USHORT feature) 
866 {
867   switch(feature) {
868     case FADF_UNKNOWN:  return TRUE; /* those are pointers */
869     case FADF_DISPATCH: return TRUE;
870   }
871   return FALSE;
872 }
873
874 /************************************************************************ 
875  * Used to calculate the displacement when accessing or modifying 
876  * safearray data set.
877  *
878  *  Parameters: - LONG *coor is the desired location in the multidimension
879  *              table.  Ex for a 3 dim table: coor[] = {1,2,3};
880  *              - ULONG *mat is the format of the table.  Ex for a 3 dim
881  *              table mat[] = {4,4,4};
882  *              - USHORT dim is the number of dimension of the SafeArray
883  */
884 static ULONG calcDisplacement(
885   LONG           *coor, 
886   SAFEARRAYBOUND *mat, 
887   LONG           dim) 
888 {
889   ULONG res = 0;
890   LONG  iterDim;
891
892   for(iterDim=0; iterDim<dim; iterDim++) 
893     /* the -mat[dim] bring coor[dim] relative to 0 for calculation */
894     res += ((coor[iterDim]-mat[iterDim].lLbound) * 
895             endOfDim(coor, mat, iterDim+1, dim));
896
897   TRACE("SafeArray: calculated displacement is %lu.\n", res);
898   return(res);
899 }
900
901 /************************************************************************ 
902  * Recursivity agent for calcDisplacement method.  Used within Put and 
903  * Get methods.
904  */
905 static INT endOfDim(
906   LONG           *coor, 
907   SAFEARRAYBOUND *mat, 
908   LONG           dim, 
909   LONG           realDim) 
910 {
911   if(dim==realDim) 
912     return 1;
913   else 
914     return (endOfDim(coor, mat, dim+1, realDim) * mat[dim].cElements);
915 }
916
917
918 /************************************************************************ 
919  * Method used to validate the coordinate received in Put and Get 
920  * methods.
921  */
922 static BOOL validCoordinate(
923   LONG      *coor, 
924   SAFEARRAY *psa) 
925 {
926   INT   iter=0;
927   LONG    lUBound;
928   LONG    lLBound;
929   HRESULT hRes;
930
931   for(; iter<psa->cDims; iter++) {
932     if((hRes = SafeArrayGetLBound(psa, iter, &lLBound)) != S_OK)
933       return FALSE;
934     if((hRes = SafeArrayGetUBound(psa, iter, &lUBound)) != S_OK)
935       return FALSE;
936  
937     if(lLBound == lUBound) 
938       return FALSE; 
939    
940     if((coor[iter] >= lLBound) && (coor[iter] <= lUBound))
941       return TRUE;
942     else
943       return FALSE;
944   }
945   return FALSE;
946 }  
947
948 /************************************************************************ 
949  * Method used to calculate the number of cells of the SA
950  */
951 static ULONG getArraySize(
952   SAFEARRAY *psa) 
953 {
954   USHORT cCount; 
955   ULONG  ulWholeArraySize = 1;
956
957   for(cCount=0; cCount < psa->cDims; cCount++) /* foreach dimensions... */
958     ulWholeArraySize *= psa->rgsabound[cCount].cElements;
959
960   return ulWholeArraySize;  
961 }
962
963
964 /************************************************************************ 
965  * Method used to handle data space dupplication for Copy32 and CopyData32
966  */
967 static HRESULT duplicateData(
968   SAFEARRAY *psa, 
969   SAFEARRAY **ppsaOut) 
970 {
971   ULONG    ulWholeArraySize; /* size of the thing */
972   LONG     lDelta;
973   IUnknown *punk;
974   BSTR   pbstrReAllocStr = NULL; /* BSTR reallocated */
975
976   ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
977   
978   SafeArrayLock(*ppsaOut);
979
980   if( isPointer(psa->fFeatures) ) {  /* If datatype is object increment 
981                                         object's reference count */
982
983     for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
984       punk = *(IUnknown**)((char *) psa->pvData+(lDelta * psa->cbElements));
985
986       if( punk != NULL)
987         IUnknown_AddRef(punk);
988     }
989
990     /* Copy the source array data into target array */
991     memcpy((*ppsaOut)->pvData, psa->pvData, 
992       ulWholeArraySize*psa->cbElements);
993
994   } else if( psa->fFeatures & FADF_BSTR ) { /* if datatype is BSTR allocate 
995                                                the BSTR in the new array */
996
997     for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
998       if(( pbstrReAllocStr = SysAllocString(
999             *(BSTR*)((char *) psa->pvData+(lDelta * psa->cbElements)))) == NULL) {
1000
1001         SafeArrayUnlock(*ppsaOut);
1002         return E_OUTOFMEMORY;
1003       }
1004
1005       *((BSTR*)((char *) (*ppsaOut)->pvData+(lDelta * psa->cbElements))) = 
1006         pbstrReAllocStr;
1007     }
1008
1009   } else { /* Simply copy the source array data into target array */
1010
1011     memcpy((*ppsaOut)->pvData, psa->pvData, 
1012       ulWholeArraySize*psa->cbElements);
1013   }
1014
1015   SafeArrayUnlock(*ppsaOut);
1016
1017   return S_OK;
1018 }
1019